Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
- ActiveRecord
- AmazonRedshift
- API
- AppleScript
- AWS
- AWSLambda
- CGO
- Chezmoi
- CI
- Clojure
- Cloudflare
- CloudflarePages
- CssParser
- CтохастичнийТаймтрекер
- DNS
- Docker
- Dotfiles
- Fly.io
- GCP
- Git
- GitHub
- GitHubActions
- Go
- Golang
- GTD
- HomeAssistant
- Hugo
- I18next
- JavaScript
- JSON
- Kafka
- MacMiniВДорозі
- MacOS
- Markdown
- Mastodon
- Obsidian
- ObsidianCanvas
- OmniWOPE
- OpenSearch
- Oura
- Ping
- Plausible
- PostgreSQL
- ReactNative
- Redis
- RSS
- Ruby
- RubyOnRails
- Sintra
- SMTP
- SQL
- SQLite
- Svelte
- Swift
- SwiftData
- SwiftUI
- Telegram
- Terraform
- TLS
- TypeScript
- Vercel
- VPN
- WeightPlot
- WordPress
- XCode
- Адвент2024
- Бази Даних
- БДС
- Безпека
- Блокнот
- Вебтехнології
- ВолюІнформації
- Гаджети
- ДизайнМовПрограмування
- Ігри
- Інструменти
- ІнтеграційніТести
- Кава
- КеруваннняЗадачами
- Кодогенерація
- Криптографія
- Локалізація
- Маркетинг
- МетаПост
- МоїПроєкти
- Навігація
- Оптимізація
- ОсновиІнтернетБезпеки
- Помічник ШІ
- ПомічникШІ
- ПостПроПохід
- Програмування
- Продуктивність
- Проєкти
- Проза
- РДУГ
- Рівночасність
- РобочийКомп
- Розробка
- РозумнийБудинок
- СинПопросивПриготувати
- Сон
- СтохастичнийТаймтрекер
- Танці
- ХмарніТехнології
07.01.2026
Проблема гуркітливого табуна
(Як звучить, га? Thundering herd, може, чули.)
Так називається ситуація, де багато споживачів одночасно звертаються до одного обмеженого ресурсу.
Певно, найтиповіший випадок — одночасний старт багатьох копій сервісу. Та, припустимо, цей сервіс зчитує з бази складну конфігурацію. Якщо сервіс один, все проходить непомітно. Але з декількома копіями база несподівано лягає. Під копитами гуркітливого табуна.
А якщо ви покладете конфігурацію в кеш, проблема не те щоб зникає. Бо все одно всі звернуться до бази, аж поки перший запит не завершиться та в кеші не зʼявиться значення. Так само буде й після вичерпання терміну придатності кешу.
Інший приклад: класична помилка планувати повторювані задачі на початок дня чи години. Чітко на нуль хвилин. Таке призводить до раптового вичерпання ресурсів системи. (Зокрема, поштові сервіси отримують пікове навантаження саме в рокові нульові хвилини.)
Порятунок від гуркітливого табуна, зазвичай, це розосередження викликів. Впровадження маленької випадкової розбіжності: 1 min + rand(5) s. Уникнення монотонності. Словами Франка Герберта, walk without rhythm and it won’t attract the worm.
Є й інший, значно складніший вихід. Але він допоможе, коли виклики відбуваються непередбачувано. Це згортання запитів (request coalescing або request collapsing.) Власне, потрібний посередник, який буде відстежувати однакові та одночасні запити, робити лише один, та роздавати результат усім. Наприклад, цим може займатися nginx з директивою proxy_cache_lock.
06.01.2026
OmniWOPE - тепер в Discord
На новорічних канікулах додав можливість виводити в Discord до OmniWOPE - мого рішення для кроспостингу.
Хотів ще до Patreon постити, але там, несподівано, не знайшлося для цього API. Постити можна тільки вручну. Ну, нічого не поробиш.
А в Discord все достатньо просто — є JSON API. Ним можна створювати пости із форматуванням, які чудово лягають під мій контент. Щоправда, доведеться кожному користувачу створити для себе застосунок та бота — адже запускається скрипт на їхньому компʼютері. Ну але то таке, розвага на один раз.
Звісно, ще простіше було реалізувати все через LLM, бо я практично не писав коду сам. Поточний підхід — ретельно пропрацювати план в Markdown, а потім запускати на виконання. (Це 100% тема для майбутнього воркшопу.) Причому LLM здатна пропрацювати всі аспекти — і дослідження, і тестування, і написання інструкції.
З цікавих моментів була ідентифікація каналу. Спочатку я збирався вказувати в конфігурації назву каналу. Бо ID каналу в UI ніде не світиться. Але назви недостатньо без ID спільноти (яка в API називається guild, до речі.) Тому знайшов прямий та в ретроспективі очевидний підхід: з каналу легко скопіювати посилання для поширення; це посилання і містить ID як каналу, так і гільдії. Люблю, коли URL дійсно є Універсальним Локатором Ресурсу, а не чимсь там іншим.
Хотілося б ще додати вихід у поштову розсилку. Може, навіть через Mailtrap.
05.01.2026
Страх, лінь та уникнення проєктів
Одна з ключових думок у GTD - те, що замість списку задач в нас зʼявляється окремий список проєктів та список наступних дій. Різниця в тому, що класичний список задач часто містить недодумані пункти, які неможливо виконати: або вони надто складні, або є невиконані та неявні передумови.
За GTD ми таку задачу перетравлюємо та окреслюємо двома протилежними точками: проєкт — це віха майбутньої мети, а наступна дія — це те, що ми можемо фізично виконати вже зараз. Та, нормативно такий підхід застосовується до кожної задачі, що перед нами стоїть.
Але. Завжди зʼявляються дрібні задачки, які ну неначе ніяк не потребують стільки церемонії. До того ж поняття проєкту в мене досі несе відчутну ментальну вагу, бо з одномоментної “задачки” набуває тривалість та місце в житті.
Тож я регулярно записую такі задачки в щоденний списочок, а не в систему. Деякі з них дійсно робляться швидко. А от інші застряють на багато днів. Починаєш думати реактивно, “треба сьогодні вже взятися і записатися на мийку”. Та не помічаєш, що це буквально першопричина практики GTD! Адже на мийку я не записуюсь, бо улюблена закрилася, а іншу ще треба обрати! Не можу я “записатися на мийку”, скільки б не розставляв пріоритети.
Вихід тут такий: до списку дрібних задач ставитися, як до списку наступних дій. Буквально. Якщо під час розбору задача дійсно складається з однієї фізичної дії — то чом би й ні, нехай буде. А як зʼявився “відрізок проєкту” - то прийми, що в тебе вже є складна задача — не в GTD, а в обʼєктивній реальності, та найкоротший шлях до її виконання — це розібрати, покласти у відповідні списки… та рухатись!
29.12.2025
Розкласти по фактах
Ще раз дякую за тему пану Степану.
В IT існує певний культ фактів. Де факти — там правда. Розмова про факти набуває академічної ваги. Люди, які оперують тими фактами — видаються неемоційними, неупередженими, та вартими довіри. Проблема в тому, що часто це тільки прикриття.
Інколи людина вже обрала рішення, та хоче, щоб його прийняли. Але асертивно сказати “я хочу зробити так і так” не вміє. Тоді факти стають маніпуляцією. Їх обирають так, щоб рішення було привабливим. Ключовим тут є те, що рішення вже обране. Та часто цей вибір обґрунтований зовсім іншими факторами — насамперед власними уподобаннями та досвідом.
Коли є двоє з суперечливими рішеннями — починається ще й боротьба фактами-маніпуляціями. Хто краще подасть факти — той і переможе. Це може виглядати, як чесний аналіз — втім, оскільки всі рішення вже обрані, факти не змінять нічию думку, а лише здатні задавити її.
Насправді коли рішення немає та впевненості немає, може трапитись ще гірше — факти служать прикриттям до цієї невпевненості. Я особисто ненавиджу приймати рішення суто по фактах. Мені треба, щоб я відчував, що роблю правильно. Та, звісно, факти можуть допомогти набути це відчуття. Але поки я дивлюся на варіанти та бачу про них тільки факти — обирати рано.
🎄 А зараз я йду в новорічну відпустку. Наступний пост буде 5 січня. З прийдешнім Новим роком вас! 🎅
26.12.2025
Quake
В моїй уяві є архетип шутера, з яким я порівнюю всі інші — свідомо чи несвідомо. Це - Quake 1. Де в кого Doom - це перший “справжній шутер”, але я гадаю саме Quake набув справжньої сучасної форми. Не кажучи вже про те, що саме рушій Quake лежить в корні численних сучасних ігор.
Quake швидкий, стрибучий, тут немає укриттів та вся безпека в рухомості та агресії. Це, в цілому, та ж гра, що й Doom Eternal - тільки зведена до мінімалістично ідеальної формули.
Саме на Quake 1 я навчився WASD. Саме там був мій перший мультиплеєр — в школі, в той час, коли ми повинні були розвʼязувати олімпіадні задачі.
Потім на Quake я вивчав 3D-графіку, дізнавався, що таке BSP (бінарне розбиття простору), а також .bsp - формат файлів, в яких Quake зберігає рівні. А тепер можна взагалі код його вивчати.
Ще двадцять з гаком років тому на Quake була сцена модів, деякі з них повністю змінювали суть гри, наприклад, перетворювали її на фентезі чи на військовий шутер. Але смішно, що моди не могли замінити скрипти поведінки, тому в ворогах завжди впізнавалися знайомі фігури (особливо той характерний людожер з бензопилою та гранатометом.)
Звісно ж, Quake живий й досі. Та досі чарує своєю простотою — це така гра, в яку легко встрибнути. Можу порадити рушій Quakespasm (навіть на мак можна!) та сайт Slipseer з модами. От відразу раджу Brutalist Jam.
25.12.2025
Надгробки в базах даних
ОК, ще одна повʼязана ідея, з якою мені чомусь постійно доводиться стикатися останнім часом.
Річ у тім, що видалення з бази даних практично ніколи не вивільняє місце запису. Натомість запис помічається як видалений — утворюється так званий надгробок (tombstone). І тільки пізніше, окремою операцією очищення бази це місце буде дійсно вивільнено. Інколи таке очищення відбувається лише вручну… або за певної архітектури взагалі не є можливою.
Те ж саме стосується й редагування. Бо найчастіше редагування відбувається через “видалення” попередньої версії та дописування нової. Тим самим у нас теж зʼявляється надгробок. На то є кілька причин — в транзакційній базі головна причина те, що нам все одно треба зберігати й стару версію теж. Так само і в розподіленій базі.
От якщо взяти PostgreSQL, то надгробки видаляються операцією VACUUM. Яка може виконуватися й автоматично. Може, а якщо це не налаштовано, то цілком реально опинитися з табличкою, де мертвих записів на порядок більше, ніж живих.
В ElasticSearch все складніше; найкращій та рекомендований варіант — це просто видаляти весь застарілий індекс. Бо інакше його доведеться принаймні заморожувати на запис. Тому навіть використовується такий підхід, що оновлені документи пишуться в нові індекси, а не в один.
Інколи надгробки є більш… матеріальними, от в CouchDB їх роблять заради синхронізації видалень між вузлами. Та й взагалі будь-де синхронізація вимагає надгробків.
А інколи ми робимо їх самі - додаємо поле deleted_at.
24.12.2025
Що ж воно таке за дерево той індекс?
ОК, давайте відразу виправлюсь: все ж індекси використовують Б-дерева, а не двійкові дерева. Але в чому різниця?
Двійкове дерево то є просто дерево, де в кожного вузла не більше двох дітей. Є двійкові дерева пошуку, де ці діти (піддерева) містять"елементи менші за поточний" та “більші”. Є збалансовані дерева, де піддерева мають рівний (чи приблизно рівний) розмір. (Бо наївна побудова дерева може дати зовсім не рівний та не ефективний результат.)
А Б-дерево — це як збалансоване двійкове дерево пошуку, тільки замість того щоб тримати в кожному вузлі тільки один елемент, тепер їх стає N - та так само як у двійковому дереві, ці елементи ділять простір пошуку на піддерева — але їх вже не два, а N+1. Відповідно, дерево стає “щільнішим”, вузлів в ньому стає менше, як і глибини.
А чому, власне, взагалі для пошуку беруть дерева, а не щось інше? Бо за деревом можна не тільки швидко шукати, а й швидко вносити зміни. (Швидко — це зі складністю O(log N).) Б-дерева саме й винайшли для прискорення пошуку у великих файлах. Причому з такою вимогою, щоб можна було завантажувати в памʼять лише частину дерева.
Б-дерева настільки прижилися, що зустрічаються майже в будь-якій базі даних. Їх можна знайти й у звичайних структурах даних різних мов - JavaScript, Go, Rust тощо. Трохи кумедно, що вони здаються складнішою структурою, втім насправді ми користуємося ними постійно — за лаштунками простих абстракцій.
23.12.2025
Що взагалі таке індекс у базі даних?
От кажу я — індекс той, індекс цей, з тим допомагає, з тим не допомагає, а що взагалі є індекс?
Дані в базі ніколи не розташовані в порядку, оптимальному для їхнього пошуку. Почнемо з того, що було б надто дорого впорядковувати дані після кожної зміни. А також, критеріїв пошуку може бути багато, а впорядкуємо ми лише під один. Та й це взагалі має сенс лише якщо записи мають рівний розмір… Одним словом, так ніхто (майже) не робить.
Натомість ми робимо індекси. Індекс — це структура даних, оптимізована для певного пошуку. Нічого магічного тут немає. 99% всіх індексів — двійкові дерева. Бо в них добре шукати за точним збігом та за порівнянням. Ключами в цьому дереві є значення, за якими ми індексуємо, а значеннями — певна фізична адреса відповідного запису.
(Тобто так, в кожного запису дійсно є адреса, за якою можна швидко його знайти без всякого індексу. Але вона нам ніяк не корисна.)
Окрім двійкових індексів, є й більш спеціалізовані — наприклад, геопросторові індекси, за якими можна швидко знайти місця за географічними координатами. Чи повнотекстовий індекс. Але це все суто винятки.
Або я писав про триграми - вони будуть у двійковому дереві, тільки побудованому за шматочками значень, а не цілими значеннями. Але все одно, інтуїція така, що індекс — це двійкове дерево за певним полем. (І тут стає питання, а що таке двійкове дерево?)
22.12.2025
Домашня панель
За вихідні (насправді, ранок неділі) навайбкодив собі домашню панель, як і збирався цього тижня. “Навайбкодив” в цьому разі значить — не писав код самостійно. Та навіть майже не перевіряв його. Чесно, на більше немає часу, а панель потрібна — зокрема, щоб розуміти, чи електрика йде з мережі або з резерву.
Отже, який в мене зараз підхід. Спочатку я десь параграфом пояснюю, що я взагалі хочу. Здійснюю невеличкий мозковий шторм. Та в кінці пишу - “зроби мені plan.md”. Агент генерує дуже довгий та детальний план, в якому, зокрема, будуть можливі питання або рішення. Відповідаю на них. Для того виділяю питання у файлі, надсилаю в чат, та коментую. Агент заміняє питання на відповіді.
Зрештою план доповнюється всілякими подробицями — які API використовувати, які вимоги до надійності, як робити UI тощо. Як план буде мене задовольняти — відкриваю новий чат та кажу “роби за plan.md”. Воно робить з першого разу вже дуже непогано.
Ну, а потім сиджу та послідовно покращую те, що бачу. Це для мене дуже нагадує роботу в парі: я кажу, що робити, а LLM робить. На кожну нову тему створюю новий чат.
В цьому проєкті відкрив пару нових для себе режимів роботи з агентом. В одній з інтеграцій для HomeAssistant був критичний для мене баг. Пощастило, що вже є PR з виправленням. Тож кидаю агенту адресу того PRу та кажу - “застосуй його до мого HomeAssistant!” Агент зміг підʼєднатися за SSH до машини з HA, знайти відповідні файли та застосувати зміни. І це суто вбудованими можливостями Cursor, без всяких MCP.
Другий випадок схожий: ця панель розгортується на локальну машину, де стоїть сервер Lighttpd. Я в ньому не знаюся зовсім. Тож доручив агенту згенерувати скрипт, а потім ще й розібратися, чому скрипт не працює. Він також за SSH зміг виявити, що в мого Lighttpd вимкнений mod_rewrite та увімкнути. А я дивлюся та розумію: я б тільки на цю проблему вбив вечір.
А ще ця панель і вебсокетами користується, і відновлює звʼязок після відключення, і має маленьку сторінку налаштувань. У проєктах-хобі, завдяки ШІ мрії здійснюються, та я цьому не припиняю радіти.
19.12.2025
Надсилання SMS через API: ось, що я дізнався
Для проєкту сповіщень про тривоги обрав сповіщення саме через SMS. По-перше, це не покладається на інтернет, якого частіше може не бути. По-друге, в айфоні можна зробити SMS з конкретного номера такими, що будуть давати сигнал навіть в тихому режимі. (Радий буду почути й ваші аргументи.)
Але я ніколи не займався надсиланням SMS серйозно, тож які тут знайшов можливості? Вимоги в мене трохи специфічні, бо я не планую надсилати понад 500 SMS на місяць, причому лише на 2-3 номери. Більшість сервісів орієнтовані на розсилки.
Пішов до найвідомішої компанії спочатку, тобто Twilio. (Це ті, кому належить Sendgrid, але починали вони з SMS.) Кредит їм вніс. Та тут виявилося, що SMS на українські номери тут коштує близько 50 грн. Ну якось забагато для хобі, що вам сказати. До того ж українських номерів вони не дають, хоча то вже справа друга, аби надсилали. Зате в Twilio є Go SDK.
Коли побачив ті ціни, знайшов українську альтернативу - TurboSMS. Тут вже близько 1 грн за SMS. Є HTTP API. В цілому, воно працює! Але: тут надсилання йде не з телефонного номера, а з іменованого відправника. Імена ці проходять реєстрацію в Національному реєстрі, то ж це ціла довга історія, яка мало зрозуміла для мого проєкту.
Щоб не реєструвати імʼя, можна скористатися одним з вбудованих: наприклад, InfoClub. (Це пояснює, чому я часто бачу сервісні SMS від різних сервісів, але з одного відправника. З 2FA так часто буває.) В цілому, робоче рішення, але зʼявилася інша проблема: айфон не дозволяє додати іменованого відправника в контакт. А значить, налаштувати завжди гучні сповіщення для нього не вийде.
Після того як зрозумів обмеження іменованого відправника, пішов шукати сервіси віртуальних телефонів. Такого теж багато! Поки опинився на Zadarma. Тут ти обираєш звичайний номер — як коли купуєш сімкартку. Потім цей номер стає доступним для отримання та здійснення дзвінків та SMS. Тут теж є HTTP API. Проте теж — перевірка документів перед тим, як віддадуть номер. Сподіваюся, за кілька днів відзвітую про остаточний успіх.
PS: а ще надсилання SMS всюди вимагає підтвердження особи — певно, щоб запобігти спаму.

