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

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

Підписатись на RSS · 📢 Канал в Telegram @stendap_sogodni

02.10.2024

Опублікував pbcopy-chromium; Swift Package Manager

Нарешті дійшли руки опублікувати мою утиліту для копіювання з Obsidian Canvas. Тепер вона доступна на GitHub. Трохи деталей про публікацію, бо це мій перший проєкт з відкритим кодом на Swift.

До того я збирав та запускав проєкт в XCode. А потім скопіював виконуваний файл собі в каталог скриптів — все працювало добра. (Взагалі, попри те, що застосунки macOS мають розширення .app та є текою, а не файлом — всередині кожного все одно сидить виконуваний файл.)

Але для публікації хотілося щось чистіше. Знайшов оцю статтю. Стаття раптом відкрила для мене цілий новий тулчейн. Бо, як виявляється, XCode мені абсолютно не потрібний для цієї задачі — достатньо оголосити конфігурацією файлом Package.swift та збирати командою swift. Ось так, давно про це думав та несподівано перейшов до “відкритого” тулчейну, яким можна будувати й інші невеличкі утиліти.

Залишався тільки один нюанс: щоб програма для macOS під час запуску не викликала застережень, її необхідно підписати, тобто нотаризувати. На це теж знайшов [статтю](Build a notarized package with a Swift Package Manager executable – Scripting OS X) та з нею команду codesign. Теж нічого складного. Хоча в мене вже був сертифікат, який був отриманий через XCode.

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


01.10.2024

Ще SwiftUI на десктопі

Ще трошки вражень від розробки на SwiftUI для macOS. Я потроху роблю свій застосуночок для GTD, та зізнаюся, це значно легше, ніж робити щось аналогічне для iOS.

Вже не знаю, чому, але в мене давно не було інтересу до розробки десктопних застосунків. Можливо, через те, що я професійно пишу для вебу, та десктоп виглядав як щось зайве. Можливо, від мобільних застосунків більше вау-ефекту та вони більше на слуху. Але в мене якось не було ідеї робити щось для macOS. Взагалі останній раз серйозні застосунки робив ще на Windows, тобто років 15 тому.

Втім, зараз я впевнений, що якщо вивчати Swift / SwiftUI, то варто робити це на десктопі, а на мобільні застосунки переходити вже з досвідом.

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

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

Swift(UI) майже весь переношуваний на телефон (та й навіть на годинники та телевізори, частково.) Тому набуті навички теж переносяться. Дуже вдале виходить рішення. Навіть краще за React(Native), бо презентаційний код спільний. Хоч у Swift(UI) все ще залишається низка підліткових недоліків.


30.09.2024

5 правил програмування Роба Пайка

Я не раз посилався в цьому каналі на правила програмування Роба Пайка.

Роб Пайк — якого я знаю як одного з засновників Go - взагалі людина з багатою карʼєрою — він доклав руки до Plan 9, Unix, UTF8 та інших проєктів. А правила ці були записані ще у 1989. Це один з документів, який я постійно згадую у своїй роботі. Наведу їх тут з моїми коментарями.


29.09.2024

Чому робота складніша за олімпіади?

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

Наведу приклад дуже складної задачі з мого нещодавнього досвіду. Потрібно було розробити систему бізнес-правил. Одні правила були загальні для всіх випадків; інші покладалися на конфігурацію. Параметри правил могли належати до однієї з 4 сутностей. Сутності були вкладені та це впливало на вибір правила. До всього цього повинен бути зрозумілий інтерфейс, який адекватно покривав можливі комбінації параметрів.

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

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

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

PS. А ще про олімпіади згадували дружні канали тут та тут, доєднуйтесь й ви.


28.09.2024

Олімпіадне програмування

Мій шлях програмування почався в школі з двох речей. Перша - ігри для DOS. Друга — олімпіадне програмування.

Олімпіадне програмування не схоже на 99.99% моєї карʼєри (А чи є галузі, де це не так? Пишіть, якщо знаєте.) Більшість задач хоч і можна розвʼязати прямолінійно (але не всі) - проте абсолютно завжди тривіальне рішення (той самий brute force, або “повний перебір”) не впорається за поставлені рамки часу або памʼяті. Тестові приклади міститимуть достатню кількість даних, щоб це перевірити.

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

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

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

Якщо хочете спробувати, пропоную сервіс USACO Training. Якимсь чином він залишився живим ще з початку 2000-х. Там гарна драбинка задач та посилання на інші ресурси, а писати розвʼязки можна навіть на Python та Java.


27.09.2024

Флешкартки Anki для запамʼятовування колег

Флешкартки “обличчя => імʼя”? Чом би й ні! Вийшов напівкріповий, напівкорисний помічник для корпоративів. А ще це такий проєкт, що й не дуже поділишся, бо залежить від вашої специфіки. Так що ділюся ідеєю.

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

З браузера в сеансі користувача теж не так легко викликати API - очевидно що це можливо, проте fetch напряму не запрацював. Тоді знайшов обхідний шлях: є тестер API, в який можна встромити ключ, забраний з сеансу браузера, та тоді тестер цілком нормально віддає результати. Це хоч і не автоматичний шлях, але принаймні можна забрати весь перелік користувачів за 2 запити. (А ще в того переліку немає фільтрів, окрім як за командою. Навіть видалених користувачів не можна сховати.)

Тепер до Anki. Чомусь красивої офіційної інформації про формат не знайшов. Хоча всередині сидить просто база SQLite та файли картинок. Знайшов гем anki2, яким легко згенерувати той файл .apkg, який імпортується в Anki. Формат карток — ніби HTML-lite. Єдине нарікання — що гем генерує випадкові ідентифікатори карток, тож коли перегенеруєш теку, то створюються дублікати — хоча сам Anki вміє домішувати нові картки.

До речі, я користуюся мобільним Anki для iOS, він дорогущий, але зручний - зокрема автоматичною реалізацією розподіленого повторення. В цілому флешкартки рекомендую для вивчення будь-чого.


26.09.2024

Торт

🎂 Син замовив на день народження торт їжачок. А потім я здогадався спитати, з чим робити його та отримав список інгредієнтів: горіхи, кава, тирамісу, крем як в київському торті, коктейльна вишня, ананас… лікер Блю Кюрасао.

Це звучить трохи неможливо (та я спочатку пошкодував що спитав), але насправді навіть з такими вимогами можна працювати. Торт складається з: коржів, крему внутрішнього, крему зовнішнього, прикрас. Залишається знайти рецепти для кожної частини.

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

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

(Взагалі лікер надто слабкий для крему — на смак та за кольором. А багато його не додати через алкоголь. Колір я виправив барвником, але для смаку гарно було б додати апельсинову есенцію або бітер.)

Проте найскладніший для мене момент настав, коли треба було перетворити торт на їжачка. Як кумедно, що підбір рецептів та розвʼязання вимог — це моє — як на роботі — а в дизайн лізти не хотілося. Тут допомогла дружина, все що бачите зверху то її старання. Моє, інженерне, все всередині. Зате було смачно! 🦔


25.09.2024

Рефакторинг в чистому полі

Всі красиві інструкції з розробки починаються з постановки результату та кроків до нього. Але в мене часто трапляються задачі, де результат визначений дуже абстрактно. Наприклад: замінити стару бібліотеку для логінгу на нову. Або: впровадити систему суворої рівночасності для Swift 6.

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

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

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

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


24.09.2024

Виконання вимог клієнтів

У продуктах B2B завжди доводиться робити чимало змін суто через вимоги того чи іншого клієнта. Це абсолютно нормально. Мистецтвом інженера тут є впровадження змін так, щоб не перетворити код в мішанину не повʼязаних шматочків логіки.

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

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

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

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


23.09.2024

Семафори

🚥 Подолав сьогодні найскладніший для рівночасності модуль. То є мій порт GIF2MP4.swift, якій побудований на бібліотеці AVFoundation. Вона ще не була перероблена не тільки на async/await, а, здається, навіть на Swift (бо більшість бібліотек Apple для Swift почали життя як прямий порт з Objective C, а потім були переосмислені на ідіоматичний Swift.)

Проблемний код був навколо класу AVAssetWriterInput - він збирає відео по кадрах. Зрозуміло, що це споживає багато ресурсів, тому кадри ми передаємо в міру потреби. Для того є метод requestMediaDataWhenReady. Умовно, ми передаємо йому “тіло циклу”, який генеруватиме кадри.

Все б добре, але у Swift 6 більше не можна передати в той блок ані джерело кадрів, ані навіть змінну-лічильник — нічого змінного. Дилема: як зробити цикл без стану?

Я придумав інвертувати цю інверсію керування. В мене тепер requestMediaDataWhenReady тільки перемикає семафор. А в головному методі звичайний цикл по кадрах, з одним нюансом: перед ітерацією чекає на семафор.

Вийшло так. Мені навіть подобається! Причому головний метод є синхронним та не потребує колбеку. Нагадує Ruby. Звісно, цей метод блокує аж поки не перекодує все відео, тож він має сенс тільки всередині рівночасного коду (а мені тільки це і треба).

(До речі, ці обʼєкти DispatchSemaphore можна передавати в інший потік, що дуже логічно. А ще є Atomic - контейнер для передачі значень.)

А ще цікаво, що код з семафорами повільніше за async/await, оскільки у Swift await здатний робити “синхронний” виклик - якщо є можливість — а семафор завжди є повільною зміною контексту. Ну, у моєму випадку рішення з async/await неможливе, тож маємо що маємо.