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

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

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

25.06.2023

Самоорганізація з блокнотом, раунд 2

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

Система складається приблизно з таких компонент:


24.06.2023

Список проєктів та майбутнього в GTD

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

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

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

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

Мені здається, що це більш правильна інтерпретація GTD, бо вона виправдовує існування “проєкту” як зафіксованої точки, в яку ти рухаєшся. Якщо все “дієздатне” потрапляє в проєкти, та немає різниці, почати новий проєкт або закінчувати старий, то можна обійтися й простим списком задач.


23.06.2023

Повідомлення про відключення AWS, або хто наглядає наглядачів?

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

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

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

Для початку, варто налагодити моніторинг, який знаходиться “на відстані” від головного додатка. Це може бути сервіс healthchecks.io. Або моніторинг в іншому регіоні AWS (бо відразу два регіони рідко відключаться.) Або, ще краще, моніторинг з іншого хмарного провайдера (Google або Azure.)

Багато робити не обовʼязково, аби була базова перевірка. Ось ідея на пʼять хвилин: налаштувати Google Alert на запит aws outage site:news.ycombinator.com.


22.06.2023

Tab9

Я, взагалі кажучи, не дуже зацікавлений в використанні сучасних генераторів коду за допомогою нейронних мереж на кшталт Github Copilot. В мене нечасто зустрічаються задачі, яки вимагають створення однотипного коду (якщо це не кодогенерація.)

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

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

Також дуже важливо для мене, що TabNine працює локально. Головним чином, тому, що я не переймаюсь про безпеку кода — свого та клієнтів. Хоча це й ненажерлива на оперативну пам’ять функція — кожний відкритий проєкт споживає близько 500 Мб, а проєктів в мене відкрито від 4 та більше. (Локальний режим треба увімкнути окремо, та він не підтримує “просунутих”, тобто довгих, доповнень.)


21.06.2023

Версіювання кешу

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

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

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

Проблема зʼявилась, коли я додав до об’єкта конфігурації ще одне поле. Тести пройшли, звісно — як юніт-тести, так й інтеграційні. А на стейджингу виявилось, що поле порожнє та повʼязана логіка падає. Це трапилось тому, що в кеші було збережена конфігурація, збережена минулою версією коду.

Для таких ситуацій є одне пряме рішення — ключ кешу маємо поміняти так, щоб новий код не міг знайти старий кеш. Класично, в ключі має міститься версія: config:v1:, config:v2: і так далі. Так старі значення будуть видалені за терміном. Цей підхід залишає роботу з кешем в одному шарі, та не розмазує обробку неочікуваних ситуацій по програмі.

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


20.06.2023

Soulver - програма для розрахунків "на папері"

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

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

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

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

Для розробки прототипів, планування ремонту чи бюджету поїздки - Soulver це те, що треба.

Ще є Numi, яка схожа, але за моїм досвідом функцій в неї менше.

PS Пост короткий, бо я встановив гру Cityscapes з Apple Arcade та підвис в неї на весь вечір. Таке враження, що гра була збалансована на типові сучасні обмеження “чекай або плати”. Але в Apple Arcade такого ніколи не буває — навіть у тих ігор, що раніше були платними, просто прибирають всі обмеження. Тому події у грі ніколи не припиняються, вона хапає і не відпускає. Проте хороша гра, простий такий будівельник міста, шкода тільки що інтерфейс розроблений очевидно для iPad.


19.06.2023

Чим парсити XML в JavaScript?

Головна логіка в OPML Doctor складається з обробки документів XML - спочатку OPML, а потім RSS. Думаю, може, цікаво, чим воно робиться.

Версія з 2015 року робила розбір окремо на ClojureScript (OPML) та на Clojure (RSS). На той час для Clojure була бібліотека data.xml, яка разом з data.zip надавала функціональну абстракцію для документа XML. Це було прикольно, але на той час ці бібліотеки не підтримували ClojureScript, тому розбір OPML на фронтенді я робив за допомогою… jQuery. Бо так, jQuery це вміє. А ще є бібліотека jayq для ClojureScript, яка спрощує роботу з jQuery.

(Взагалі багато бібліотек для Clojure/Script загортають якісь бібліотеки на Java/Script в функціональні абстракції. Іноді вдало, іноді зовсім ні. Наприклад, бібліотека clj-time мені зовсім не подобається, та ще й документації нормальної немає. Тому на цей раз обрав використати luxon - це бібліотека на JavaScript з функціональним стилем. Викликати функції та класи JS з CLJS цілком можливо та тільки трохи незручно.)

У 2023 Clojure в мене більше немає, залишився тільки ClojureScript, а бібліотека data.xml, хоч його й підтримує, виглядає напівживою. Тому я знайшов рішення на JavaScript, а саме — стандартний та вбудований в браузер DOMParser, який підтримує як HTML, так й XML документи. Він будує стандартне дерево документа. Якщо давно не бачили, то зараз з ним зручно працювати через наявність, наприклад, метода querySelector.

Якщо парсити на бекенді (в Node.js), то вбудованої реалізації DOMParser там немає. Її можна знайти в пакеті jsdom. Є ще пакет xmldom, але він не реалізує функцію querySelector, тож шукати елементи доведеться вручну. А jsdom вміє практично все, що браузер.


18.06.2023

OPML Doctor - глибока перевірка RSS стрічок

Доробив за вихідні додаток OPML Doctor та тепер публікую тут бета-версію. Поки є тільки версія для macOS. Причина в тому, що Tauri не вміє робити кроскомпіляцію, та для збірки під Windows та Linux необхідна машина або СІ.

Додаток буде корисним всім, хто читає або випускає RSS. Зараз він дозволяє перевірити колекцію стрічок у форматі OPML на проблеми з адресою, наповненням та інші. От, з моїх 200 стрічок майже половина має проблеми; в основному це старі закинуті блоги, але є й сайти, що переїхали, наприклад.

Tauri порадував своїми можливостями. Наприклад, обробку перетягнутого на додаток файлу зробити дуже легко. Також легко зробити перемикання зі світлої на темну тему. До речі, для оформлення я використав Bootstrap, в якому тільки-но зʼявилася підтримка тем. Тож від мене потрібно було тільки встановлювати атрибут html.data-bs-theme.

Збірка додатка в Tauri відбувається майже без налаштувань. Трошки повозився з нотаризацією. Річ у тім, що сучасні додатки в macOS мало підписати сертифікатом, їх також потрібно завірити “у хмарі”. Це надає додатковий захист від крадіжки сертифіката. Коли розберешся, що як налаштувати, то все працює автоматично. Зокрема потрібно в XCode згенерувати сертифікат “Developer ID Application”. (Підписка розробника в мене вже була.) Та ще довелося явно сказати Tauri, що я хочу універсальну збірку, а не тільки для Apple Silicon.

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


17.06.2023

Загортання вебдодатка у звичайний за допомогою Tauri

Сьогодні продовжив гратись зі своїм OPML Doctor. Як можна здогадатись, головний функціонал такого додатка обертається навколо завантаження RSS-стрічок, десятками або навіть сотнями. Минула версія додатка робила це з бекенду. Але мені трохи боязно запускати такий додаток в публічний доступ, бо треба буде піклуватися про захист від визискування.

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

Electron - найбільш відомий інструмент для створення додатків на вебтехнологіях. Але в нього погана репутація. Оскільки кожний додаток на Electon містить в собі Node.js та браузер Chromium, то вони й великі за розміром, й жадібні до оперативної памʼяті.

Тому є низка альтернатив, з яких мені подобається Wails та Tauri. В обох “бекенд” пишеться ефективною мовою - Wails на Go, Tauri на Rust. Обрав поки Tauri, бо вона, схоже, більш зріла. Також мені подобається, що Tauri використовує системний браузер, що додатково економить ресурси.

Перенести SPA додаток, який вже існує, в Tauri виявилось дуже просто (для цього в них є інструкція.) Навіть при тому, що фронтенд в мене на ClojureScript! Фактично все, що потрібно — це вказати Tauri, якою командою запускати сервер розробки, та на якому порті він розташований. Далі розробка ведеться так само як в браузері.

Але, спитаєте ви, як мій бекенд на Clojure перенести на Rust? Переписувати не хочеться. Тому поміркував та визначив, що все, що мені потрібно від Rust, це виконання запитів HTTP. На мою радість, така функціональність вже доступна; Tauri надає вебдодатку декілька API, в тому числі http для HTTP-запитів через Rust.

Всю іншу логіку перевірки стрічок я можу перенести в фронтенд, бо Clojure та ClojureScript - одна й та сама мова. Тільки з залежностями не все так просто.


16.06.2023

Чому у Twitter рядкові ID

Хоча ID твітів чисельні, але у їх API вони повертаються як рядки. Чому? На перетині 64-бітних ID та чисел з рухомою комою знаходиться, мабуть, найбільший Wat Джаваскрипта. А саме, те, що великі значення цілих чисел будуть округлені без жодних повідомлень та попереджень.

Причина в тому, що в Javascript є тільки один тип “число”, та це число зберігається як тип double. Оскільки тип double теж займає 64 біти, то цілочисельне значення в 64 біти в нього технічно неможливо вмістити.

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

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

Не всі мови себе так поводять; наприклад, Ruby в залежності від значення вживатиме цілі числа або рухому кому. А якщо число не влазить в int64,ʼ то перейде до довгої арифметики, тобто до представлення числа у вигляді необмеженого рядка з байтів. Довга арифметика вже 5 років є й в JavaScript - це тип BigInt, але його треба залучити явно, та сподіватись, що всі це зроблять, не можна. Ну, а з системними мовами проблем точно не буде.

Щоб такого з вами не трапилось, 64-бітні ID треба передавати у вигляді рядка. Якщо почитати документацію API Twitter, то вони саме так і пояснюють. Я б пішов ще далі та замість числа в десятковій системі зробив більш компактний формат, ну принаймні hex. Це також сприятиме використанню ID у вигляді рядка, без ризикованої конвертації в число. (Але там, де швидкодія має значення, треба перетворювати назад в int64, безумовно.)