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

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

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

01.12.2025

Танці — це Dark Souls соціальних активностей

💃🕺 Вітаю на Тижні Танців на моєму каналі! Цього року я перевідкрив для себе забуте захоплення — соціальні танці, а саме сальсу та бачату. Затягнути когось нового танцювати — приблизно так само складно, як переконати, що Dark Souls це не знущання з геймерів, а гра, яка здатна принести більше задоволення ніж будь-яка інша. Після періоду навчання, звісно. Але на цьому схожість соціальних танців з Dark Souls не закінчується.

🎲 Не вийде просто “лупасити по кнопках”. Як і Dark Souls, танці існують на взаємодії відчуттів та інтелекту. Кожний початий рух доведеться закінчувати. Темп, ритм, спостереження та влучно обрані рухи — головне. Як і в Dark Souls, тобі доведеться зазирати в майбутнє та бути готовим реагувати та ініціювати.

🎁 Куди б ти не пішов, завжди знайдеш щось цікаве. Прогресія в танці цілком залежить від тебе, та на кожному тренуванні та вечірці ти збираєш для себе нові елементи. Як і в Dark Souls, це не мета, а завжди присутній фактор відкриття та розвитку. В танцях приємно прийти в нову групу, бо навіть з одного заняття ти дізнаєшся щось нове.

♾️ Справжня Dark Souls починається у PVP. Справжні танці починаються в імпровізації. Так, танцювати заздалегідь підготовані та домовлені комбінації — так само задовольняє, як проходити знайомих босів знайомими тактиками. Але звільнись від того і в тобі прокинеться творчість.

🏋️ Як і в Dark Souls, ти є ключем до власного успіху. Немає нічого неможливого, втім немає й коротких шляхів. Тільки практика, тільки “git gud” перетворює танець зі стресового, гіпертривожного заняття новачка в безтурботний стан потоку. Та, чорт забирай, той стан потоку вартий, щоб за нього кілька разів повернутися до багаття!

🌞 Як і Dark Souls, танці — місце для веселої кооперації. Хоч перед кожним з нас стоїть власний шлях та власні випробування, ніхто не проходить його наодинці. (В соціальних танцях, на відміну від Dark Souls, буквально.) Ти можеш очікувати на доброзичливу атмосферу, підтримку та допомогу. Як і в Dark Souls, те, що тобі здається неможливим, раптом виходить завдяки вдалому партнеру.

🎳 When in doubt, roll it out. Need I say more?


28.11.2025

Головне у системі продуктивності — це перегляд

Бо без перегляду в нас немає системи. Власне, ми робимо розрізнені нотатки системою, коли беремося їх переглядати.

Раніше я гадав, що головне — це всеосяжність системи, чи ретельне дотримання того, що в ній написано. Але на практиці й те, і те — практично неможливо виконати. Та й не потрібно. Що нам потрібно — це зберігати свіжість системи та згадувати, що там записано. Решта відбувається майже автоматично.

Втім, не все так просто. Перегляд — це не просто “продивитися”. Це приділити кожному пункту належну увагу. Якщо не приділяти та не підтримувати систему у свіжості, то в певний момент очі скляніють та ти вже не думаєш про свої списки, коли на них дивишся. Це — кінець системи.

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

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

Але щоб система працювала, ти повинен чітко розуміти, як часто та з якою увагою переглядати кожен список.

Так що, з чого почати займатися продуктивністю? Завести хоч якийсь список того, що ти хочеш зробити, та переглядати його щоденно.


27.11.2025

Використання ШІ для заповнення тестів

Для мене головною областю використання ШІ є, певно, ті місця, де я сам не дотягую. Тобто Ші не заміняє мою роботу, а доповнює її, часто дуже вигідно.

От взяти нещодавню ситуацію. Треба було для Сінтри щось підправити в функції форматування діапазонів дат. В нас там дуже розумна функція, яка вміє, наприклад, 20-27 листопада, якщо дати в одному місяці. Тестів на цю функцію — немає. Бо ніколи не було часу писати тести, ви що, це ж сайд-проєкт.

Поки ти таку функцію пишеш вперше, то зрозуміло, що тестуєш по всякому вручну, та знає, що вона працює. Але якщо стає задача щось додати роки потому — то ніколи не вгадаєш, який крайовий випадок зламався.

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

Ще ШІ чомусь почав з дуже дивних перевірок регулярними виразами, хоча функція вертає короткий рядок, який можна перевірити цілком. Ба більше, я й хотів перевіряти, що результат збережеться! Ну то теж попросив переробити на пряме порівняння.

Спочатку, зауважу, я покрив тестами код, який вже існував. Навіть спеціально вказав не змінювати реалізацію! Бо задача стояла зафіксувати поведінку такою, яка вона є. А далі вже як були тести, настав час спокійно доробляти.

Гадаю, буде цікаво подивитися, що там за тести, тож ось вони. Цей код практично повністю згенерований ШІ. Але розумієте, якби не ШІ, я б ніколи не став писати ці тести — просто код був би з помилками.


26.11.2025

Замороження фактів для LLM

Ще маленьке та корисне концептуальне відкриття. Як відомо (серйозно, всім повинно бути відомо), великі мовні машини оперують над ймовірностями, а не “твердими” фактами. Зокрема, чим довше поточний контекст, тим більше факти в ньому “розпливаються”.

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

(Особливо треба розуміти, що факти в контексті будуть не тільки ті, що ми вважаємо за важливі, а й абсолютно тривіальні та зайві. Як те, чи використовуєте ви пробіли або табуляцію.)

Щоб такого не було, важливі факти можна підкласти в контекст в готовому вигляді. Так ми робимо з файлами правил, наприклад.

Але якщо взяти підхід ширше, то можна в будь-який момент зберегти знання з поточного контексту у файл. LLM залюбки згенерує вам Markdown. Цей файл вже буде легко доступним джерелом правди для майбутніх операцій. А проєкт буде обростати документацією на всілякі потреби: про те, як ми пишемо запити, тести, про те чи інше рішення.

Не знаю, чи в цього підходу є широко відома назва, але я його називаю замороженням фактів. Бо те, що записано в цих файлах, стає “твердою” правдою, яку ШІ більше не забуде.


25.11.2025

Дослідження чужого проєкту за допомогою LLM

Хочу особливо поділитися технікою, яка мене врятувала під час впровадження SQLiteData.swift. Річ у тім, що ця бібліотека не має стільки документації, скільки умовний ActiveRecord, та як виходиш за межі прикладів, то на пошук в інтернеті можна не розраховувати.

Та ось моя ідея: я склонував собі репозиторій, відкрив в Курсорі, та почав ставити питання. Ось кілька прикладів.

find examples of @fetchone or @fetchall used with parameters passed from parent components

Типовий випадок, як-от ProjectView(project: project). Як мені завантажити дії цього проєкту? Виявилося, що такі запити створюються в ініціалізаторі: _actions = FetchAll(Action.where { $0.id =project.id}).

how do you use toggle()

В документації все сумно. А з коду стає зрозуміло, що Action.update { $0.isCompleted.toggle() }

is there a way to extract statements that become `@FetchOne` and `@FetchAll`  into a separate module? i'm specifically struggling with types

Хотілося зробити DRY, але ніяк не міг зрозуміти, якого ж типу повинна бути функція, яка повертає той запит. (Не конкретний тип, який традиційно довжелезний та заплутаний, а узагальнений.) Тут ШІ мені згенерував цілий туторіал в Markdown, а тип виявився на кшталт some StructuredQueriesCore.Statement<Int>.

how can i get to the OpaquePointer db pointer needed for SQLIte3 calls from my database instance

Оце останнє на сьогодні, бо я хочу додати пошук із UNICODE_NOCASE, а розвʼязок знайшов тільки через команди бібліотеки SQLite3. А як добратися до цієї низькорівневої бібліотеки? Виявилося, дуже легко - через database.write { db in db.sqliteConnection }. Але в документації про це ані згадки.

Якщо розвинути ідею, можна було б відразу всі залежності проєкту таким чином додавати через MCP чи щось таке. Але я на таке поки не спроможний.


24.11.2025

Деякі обовʼязки лід-інженера

Що забув?


21.11.2025

Hario Largo - найкращий заварник для чаю

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

(Взагалі, може здаватися, що це завжди так було, але ні — вся сучасна культура обміркованого споживання кави існує трохи понад 20 років. Але зараз не про те.)

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

Отже, щоб не відтягувати далі: я знайшов заварник Hario Largo. Ідея проста: це колба, в якій вільно плавають чаїнки, а коли сплив час заварювання — чай зливається через клапан (та велике сито) в підготований глечик, чайник чи чашку.

З таким заварником тривіально витримати всі вимоги заварювання. Хочеш промити чай — та без проблем. Хочеш взагалі проливний, як пуер чи улун — це теж можна. Оці чайні бутони, які красиво розкриваються в чайнику — будуть всі на виду. А можна поставити настоюватись травʼяний чай на пʼятнадцять хвилин, а потім швиденько злити. Можна заварити одну чашку, а можна на цілий термос.

Мені також дуже важливо, що тут немає пластику — сама колба скляна, фільтр сталевий, основа гумова. Все розбирається та миється. Підставку, яка продається разом, я використовую і для пуровера.

Та от повірте, між просто чаєм та таким, що правильно заварений — океан різниці.


20.11.2025

Вайб-кодінг — для всього неважливого

Потрапив на очі гостренький заголовок: Лінус Торвальдс вважає що вайб-кодінг це добре, поки не використовується для чогось важливого. Звучить як відмова від всієї ідеї — окрім як в контексті навчання.

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

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

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

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

Програмісти взагалі занадто привʼязують власну цінність до коду, що повертає нас до того, що варто памʼятати, що ти інженер.


19.11.2025

Елементи безпеки в дизайні мови Swift

Не знаю достатньо про Rust, щоб прокоментувати насущне фіаско з unwrap(). Скажу тільки, що, як на мене, небезпечно називати метод, який може панікнути, такою невинною назвою.

А розповім краще про дизайн Swift - мови, яка дуже схожа на Rust, насправді. (Ви взагалі знаєте, що на Swift можна писати й серверні застосунки? Ось, наприклад, Vapor, а до нього є ORM, Postgres та все як ми звикли.)

Отже. У Swift навколо значення типу Optional. Просто повсюди! Якщо порівняти із, наприклад, Go, то в Go теж навколо “опціональні” значення, тільки перевіряти ми їх будемо неявно (як-от, якщо функція foo() (*bar, error) повернула помилку, то в *bar порожньо, а якщо ні, то щось буде, та на nil можна окремо не перевіряти.) Проґавили — отримали паніку. Ну так Go не називає себе безпечною мовою, там естетика професійного інструменту, до якого ти мусиш вивчити техніку безпеки.

У Swift, якщо лінь танцювати весь танець розгортування Optional, можемо виконати примусове розгортання: bar!. Воно викличе помилку, якщо значення немає. Але дуже важливо, що така форма візуально підкреслює наш намір. А намір тут — не “довірся мені, я знаю що роблю”, а “я відкидаю безпеку! Стережися!” І це дуже важливий елемент дизайну безпечної мови.

Так само у Swift кожна функція, де може бути викликана помилка, повинна бути підписана словом throws, а її виклик - try. Слово try дозволено тільки всередині блоку do ... catch, або всередині функції, яка сама буде throws. Або — сміливий варіант - try! - каже, що нам байдуже на те, що помилка стане критичною.

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


18.11.2025

Ідіосинкразії SwiftUI

Я дуже люблю React за його передбачуваність. Якщо створити компонент-функцію, вся вона буде виконана. Хуки теж мають передбачувані моменти виклику. React не ідеальний, але ось ця прозорість точно його сильна сторона.

У SwiftUI прозорості немає. Життєвий цикл компоненти заплутаний. Хочу я робити запит, який приймає в себе аргумент компоненти, умовно Action.where { $0.contextID == contextID }. Цілком нормальна справа. Написати це прямо в анотації @FetchAll, як каже документація — не вийде. Бо анотації не мають доступу до аргументів — хоч цей запит наче буде виконаний вже в конкретному екземплярі.

Тоді є документація про динамічні запити… це трохи не те, але працювати буде. Воно використовує метод .task. І це ще треба знати — я сьогодні дізнався — що в нього є аргумент id, який насправді працює так само як масив залежностей у React.useEffect. Тобто буде ще й оновлювати запит, якщо цей id зміниться.

Але. Якщо запит розташований в .task, він буде виконаний із затримкою та вам не уникнути початкового вигляду із порожнім результатом. Не ідеально.

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

Тільки й це не все розвʼязує, бо ініціалізатор не має доступу до обʼєктів @Environment (це як контексти в React.) Так що якщо ти раптом береш значення звідти — доведеться робити це в батьківській компоненті та передавати аргументами туди, де збираєшся робити запити.

Складно уявити, що тут теж є концепція, яка все це робить логічним, а не просто збігом обставин реалізації.

Також додам, що XCode це єдине, що дійсно змушує мене замислитись про перехід з MacBook Air до чогось… важчого. А мені не хочеться.