Канал
У Льоні сьогодні
Нотатки архітектора систем та майстра на всі руки. Що я зробив, що з того вийшло, і що це все значить.
-
Split_tests - швидші збірки на CI
Є в мене стара, але досі корисна утиліта: split_tests. Вона, попросту, розбиває довжелезний запуск тестів на декілька паралельних потоків, за набором файлів. Так збірку довжиною в годину можна виконати навіть за 10 хвилин. (На жаль, підготовка до тестів теж займає час, тому є розумна межа на кількість потоків.)
Для того потрібний тестовий пакет, який на файли можна розбити: наприклад, RSpec, або Mocha (А ось з Go вона ще не працює, хоч на Go написана — бо Go ділить тести на модулі, а не файли.)
(🦀 Як каже аксіома, до моєї реалізації на Go є реалізація з альтернативного всесвіту на Rust: split-test. Ні, серйозно!)
Колись давно я написав цю утиліту для CircleCI. А потім невдовзі там зʼявилася вбудована команда, яка робить те саме. Потім я опинився на GitHub Actions, та утиліта знову стала до нагоди — альтернатив тут немає, а ті що є, написані або на основі моєї утиліти, або схожих.
Як воно працює. Зрозуміло що ми хочемо досягнути рівного часу виконання на кожний потік. Прийнятного результату дає розділення файлів за кількістю рядків. Звісно, трапляються аномалії, де тривалість не корелює з довжиною. Тому також можна розділяти за історичними даними за тривалістю кожного тесту. Але тоді потрібно ті дані зберігати.
Або, можна знайти аномалії вручну та викликати їх окремо. Оце на мою думку оптимальний варіант. І технічно просто, і результат надійний. Зазвичай ті “аномалії” виявляються величезними інтеграційними тестами та їх в застосунку лише декілька.
-
Статусні коди SMTP в порівнянні з HTTP
У SMTP, як і HTTP, сервер відповідає статусним рядком, який складається з коду та розшифровки. (Хоч на відміну від HTTP, SMTP - це діалоговий протокол, але діалог закінчується статусом.) Ніби все дуже красиво, багато цифр та взагалі все зрозуміло:
421 4.7.0 [TSS04] Messages from a.b.c.d temporarily deferred due to user complaintsПроте є ціла низка проблем. По-перше, відповідь SMTP має більше варіантів, та нам важливо розрізняти їх. Наприклад, статус вище значить, що ми повинні відкласти надсилання на деякий час, а потім спробувати ще. Якщо ми цього не зробимо, то ризикуємо потрапити вже в постійний блок. З HTTP рідко трапляються такі нюанси, а з SMTP це нормальна ситуація. Також потрібно розуміти, чи стосується помилка конкретного листа, чи отримувача, чи відправника, чи технічних причин — на кожний випадок є своя реакція.
По-друге, між серверами SMTP немає однорідності. Наприклад, статус
421означає “сервер тимчасово недоступний”. Але у випадку Yahoo (який і видає наведену відповідь) значення інше: нам варто призупинити все надсилання пошти на декілька хвилин, щоб підтвердити власну відповідальність. Якщо ігнорувати такі рекомендації, потраплятимеш в блок.Навіть на сторінці Вікіпедії про ці статуси наведені їхні приклади, а не перелік. Максимум, на що можна розраховувати — це те, що статус
4xxтимчасовий (тобто варто спробувати пізніше), а5xxпостійний. Але це не допоможе зрозуміти, чи нам потрібно відписати адресата, чи виправляти зміст листа, чи щось інше.Щоб боротися з цим, ввели додатково розширений статус — це в прикладі
4.7.0. На жаль, з ними так само немає однорідності, тому я якщо чесно погано розумію, чим вони допомагають. Все одно, щоб зрозуміти тип відповіді, краще дивитися на весь її зміст.Але є й позитивна сторона: популярних серверів SMTP не так багато, тому цілком реально покрити їхні відповіді зі впевненістю. Навіть GMail один покриє десь третину всієї пошти. Виходить парадоксальна ситуація: хоч можливих статусів необмежено багато, але уваги потребує небагато (так, як статус
TSS04від Yahoo.) -
10 годин зачистки Jira
Весь день провів за зачисткою Jira. Це частина того, що потрібно було зробити для “чистоти думок” за GTD. (На роботі ще залишилися декілька PRів, які давно висять, та декілька десятків незавершених гілок — це в мене часта ситуація.)
Для мене Jira, як я зараз бачу — це місце для контрактів. Кожна задача — домовленість (контракт) між людьми про роботу, яку треба спланувати, виконати, перевірити та вивести у світ. Як я тільки нещодавно писав, це погане місце для ідей, поки вони не набули конкретики.
Про місце для ідей команди я ще буду думати, але поки на особистому рівні я перебирав всі задачі, до яких маю відношення, та переносив у власну канву “мабуть/колись” з належним групуванням. Також багато задач було скасовано. А декілька навіть вдалося виконати “на ходу”, бо звісно ж задачі на 10 хвилин можуть роками сидіти в беклозі без уваги.
Гадаю, для багатьох інженерів задачі в Jira заводить менеджер, а бере вона їх… з іншого місця, де відбувалося планування. Знов-таки, бо в Jira потрапляють готові контракти. Але в мене трошки інша ситуація, бо велика частини роботи “знімається з оточення”, та її треба десь занотувати та підготувати до виконання. Тому мені й потрібна додаткова зовнішня система.
До речі, мені зрозуміло, що таку зачистку потрібно робити частіше, напевно, раз на ітерацію. Коли один раз все зачистив, далі можна додати до пошукового запиту обмеження на
updatedта передивлятися тільки нове. (До речі, Jira - це база даних, та знання пошукових запитів допомагає змусити її працювати на тебе.) Потім можна експортувати результати запиту в CSV та робити з ним вже все що завгодно. Я, наприклад, генерую список, розбитий по статусах. -
Перевірка "тільки один" в Rails
Сьогодні написав одну дуже дивну валідацію. Потрібно було перевірити, що в базі є тільки один запис із позначкою “за замовчуванням”. Виявляється, для того є декларативна форма:
validates :default, uniqueness: { if: :default }Ну, бо просто унікальність не дала б створити більш ніж один “звичайний” запис. А так все працює (та проходить тести, які я написав заздалегідь.) До речі, хоч ми користуємось Shoulda Matchers (давно вже не бачив проєкту без них), але в цьому разі писав перевірку без них. Бо Shoulda Matchers зазвичай повторюють означення валідатора, а я ще його не знав. Міг тільки описати поведінку. Тобто це такий класичний приклад для TDD: коли краще знаєш, що повинно вийти, а не що потрібно зробити.
Якщо поміркувати, таку валідацію можна перенести й в базу:
CREATE UNIQUE INDEX only_one_default WHERE defaultЯк завжди, якщо база про валідацію не знає, можна опинитись в несумісній ситуації. Причому якщо після помилкового збігу двох записів можна врятуватися через застосунок, то якщо їх стане три, то застосунок не дозволить прибрати один з дублікатів — валідація не пройде. Краще не лінуватися та дублювати кожну валідацію в базі.
-
Вам потрібна надійна система "мабуть/колись"
В останній (та поки досить успішної) ітерації самоменеджменту в мене зʼявився новий елемент: надійна та зручна система для зберігання справ на майбутнє, тобто того, що в GTD називається “мабуть/колись”. В мене це Obsidian та канва, але міг би бути якийсь Notion або навіть кращі спеціалізовані рішення, як DEVONthink.
Що тут погано працює, так це програми для керування задачами. В них занадто багато структури, яка для “мабуть/колись” абсолютно надмірна. Більшість “мабуть/колись” до мене приходить у вигляді ідей та думок, які складно перекласти у задачі чи проєкти.
Наприклад: думка “зробить в шафці відділ для робота-пилосмока”. Це схоже на проєкт, та раніше всі такі ідеї в мене ставали проєктами. Принаймні, не загубиться. Але… насправді навіть шафки ще не існує, тому такий проєкт буде висіти без шляху розвʼязання. …А коли незрозумілих проєктів стане 100, система розчавиться під власною вагою.
З системою “мабуть/колись” така ідея йде в купу ідей навколо проєктування шафки. Потім, коли справа дійде до планування, всі такі ідеї можна зібрати до купи та перетворити на конкретний план дій. (Та вже на цьому етапі зрозуміти, чи той відділ взагалі потрібний, чи буде для нього місце і так далі.)
“Мабуть/колись” має фрактальний характер: більша ідея розкладається на менші (шафка сидить в купі ідей про кімнату, кімната — в купі про оселю). Десь я матиму уявлення до дрібних деталей, а в інших місцях тільки малюнок широкими штрихами. Наперед невідомо, якого рівня буде кожна ідея, тому зручно, якщо система організації підтримує таку фрактальність. Причому треба бути готовим записувати думки будь-якого рівня: щоб вони не сиділи в голові. Від “нам потрібна інша база даних” до “додати підказку до поля у формі.”
Також не можна звалювати все до купи: як-то список закладок або беклог в Jira. Ідеї повинні групуватися за змістом, щоб знайти їх в майбутньому. Це, напевно, головний аспект: коли ти додаєш думку в “мабуть/колись”, маєш бути впевненим, що повернешся до неї тоді, коли це буде актуально. Тоді можна дійсно розвантажити голову від гадок на майбутнє та сфокусуватися на поточних проєктах.
-
Obsidian: канва у список
Зробив скриптець для перетворення канви Obsidian у список Markdown. На диво, готової альтернативи не знайшов.
Скрипт спирається на звʼязки між вузлами, щоб обійти граф та побудувати дерево від кореня: вузла, на який ніхто не посилається. (Або — коренів.) На виході отримуємо список в Markdown.
Обхід поки наївний та не обробляє цикли (а точніше, якщо корінь є частиною циклу, то він не вважатиметься коренем та той цикл не зʼявиться в списку.) Також можна було б робити щось гарне з групами; вузли стають вкладеними в групу суто за розташуванням; хоч явних звʼязків немає, в списку група може бути батьком своїх вузлів.
Навіщо таке потрібно? Коли канва така хороша? Наприклад, щоб перетворити плани з канви у список задач в традиційному планувальнику. Та й взагалі, ділитися списком значно зручніше. Я можу захотіти зробити конспект у вигляді майндмепу, а потім синтезувати список та додати до загальної документації.
-
Колір та фарби
Щільно зайнявся ремонтом, тому не залишається часу ані на роботу, ані на проєкти. Втім, цікавим для мене відкриттям була фізична фарба. Бо веде вона себе зовсім не так, як на екрані.
Я, звісно, все життя стикаюся з кольором: цифровим. Система RGB - найпряміший опис кольору на екрані — перекладається прямо на інтенсивність пікселів. Система HSL - не така пряма, зате як легко з нею коригувати та поєднувати кольори. Навіть з друком все зрозуміло: там система CMYK.
Але фарба себе поводить зовсім інакше; на відміну від пікселів на екрані та крапок в принтері, фарба не є дискретною, а натомість наповнена часточками пігментів. Неможливо “надрукувати пігмент правильного кольору”, тому навіть з цифровими машинами для змішування ми все одно обмежені фізичними властивостями вхідних пігментів.
Ну, тобто звісно, теоретично ми можемо намішати нескінченно багато комбінацій. Але щоб робити це повторювано, доведеться покластись на ті тисячі кольорів, які закладені в можливості машини. В тому й лежить вся складність: фарбу не можна просто обрати за палітрою з 16 мільйонів кольорів.
Замість того доводиться брати віяло зразків… Що мене перед усім дратує у віялі — то обмеженість рухів (порівняно з RGB, де я легко знаю, як додати, наприклад червоного.) Тут допомагає знання системи NCS, в якій кольори вписані в трикутник “чорний — білий — відтінок”. Звісно, можна конвертувати RGB в NCS, але далеко не всі кольори будуть дійсно збігатися.
-
Стилі для Obsidian Canvas - реалізація
Ось перша версія стилів для канви. Не знаю, наскільки вони універсальні, бо мені більше подобається ідея стилів для індивідуальних потреб — в кого як голова працює.
Що в мене: по-перше, обовʼязково треба бачити, які вузли потребують додаткового опрацювання. Це задачі (червоним), питання (жовтим) та ідеї (синім). Далі, відзначаю закінчені (зеленим), скасовані (сірим), та заблоковані (крижано-блакитним) вузли, щоб не звертати увагу на них. Тип та стан можна поєднувати: заблоковане питання. Нарешті, для кращої організації додав великі та дуже великі вузли; вони грають роль заголовків.
Про технічну реалізацію. В Obsidian потужна система змінних CSS. Через неї, в сукупності з сучасними функціями CSS як calc та color-mix, можна вийти на власний набір кольорів. Масштабування вузлів зробив через атрибут zoom; так вони зберігають внутрішню структуру.
До речі, всі ці стилі можна додавати не тільки до текстових вузлів, а й до будь-яких — малюнків, вебсторінок тощо. Сторінки на канві я не люблю, бо це виходить повільно, до того ж більше хотілося б додавати вирізки, а не весь зміст. А малюнки навпаки, революційно зручно. Почав розуміти, чому Obsidian Sync надає 40 Гб місця — для тексту точно занадто.
Весь пакет можна забрати тут, але зручності поки на альфа-рівні. Пізніше, теоретично можна навіть написати скрипт, який зробить все повністю автоматично, оскільки всі налаштування — це зміст файлів JSON, не більше.
-
Семантичні графи в Obsidian Canvas
У форматі JSON Canvas мене відразу привабило те, що у вузлів та ребер можуть зʼявлятися власні атрибути; таким чином, канва набуває не тільки вигляду, а й змісту, тобто стає семантичною.
Наприклад, якщо порівняти моє виконання схеми BRIDGeS (див. малюнок вище) в Obsidian та офіційне в Figma, то в останньому схема має суто візуальний характер: це вузли різного кольору та приліплені до них візуально відмітки пріоритетів. А в Obsidian кожен тип вузла та кожен пріоритет стає атрибутом.
Мені зараз для планування потрібне рішення, щоб було помітно, де задача, де питання, де ідея, а де просто факт. Поки що просто придумав фарбувати активні задачі червоним, а виконані — зеленим (це єдина вбудована можливість оформлення, якщо не рахувати Markdown). Але хочу зробити власні атрибути, та за допомогою Obsidian Canvas Style Menu їх оформити.
Це стане корисним для автоматичної обробки. Наприклад, скрипт може знайти в канві всі задачі, та зробити список за пріоритетом. Також, звісно, оформлення допомагає розрізняти вузли, причому можна навіть комбінувати класи: на ілюстрації зверху дуже просте поєднання типу (за кольором) та пріоритету (міткою); але можна робити навіть на кшталт “якщо це задача та вона виконана, показати галочку.”
-
Труднощі статичного хостингу
Статичний хостинг (тобто такий, що вміє тільки віддавати файли) - ніби найпростіша річ, абсолютний commodity. Втім, обрати між ними не так легко, як здається. Все залежить від того, які у вас очікування. Але ось в чому нюанс: як тільки скористуєшся одним статичним хостингом, то очікування поведінки фіксуються, та під час переїзду доведеться з ними рахуватися (або дізнатися, що сайт працює не до кінця, тільки пізніше.)
Нюанси починаються з правил перенаправлення. Будь-який сайт стикається рано чи пізно зі зміною адрес. Будь-який чемний сайт підтримуватиме старі адреси через механізм перенаправлень. Він всюди налаштовується за власними правилами — ось, наприклад, Vercel. А ось Cloudflare Pages. Все так, та не зовсім. Доведеться вручну коригувати та перевіряти. Коли перенаправлень сотні (так трапляється після реструктуризації сайту) - це вже ціла пригода.
Деякі хостинги підтримують адреси без слеша на кінці, а інші — ні. Технічно, ніби присутність слеша “правильніша”, оскільки ми віддаємо
index.htmlдля директорії — а не файл без розширення. Але те, що адреса сторінки виглядає як директорія, це принаймні дивно. Раніше мене це обурювало, зараз вже змирився та зробив все зі слешами (Звісно, через перенаправлення.) Також не всі хостинги готові взагалі показатиindex.html(бо це вже не просто “віддавати файли”, авжеж?)Характер відвантаження файлів на хостинг теж відрізняється. Ось на Vercel примусово потрібно мати репозиторій на GitHub. Що гарно, поки в тебе немає величезних світлин або взагалі — завантажень на гігабайти. На Cloudflare Pages можна відвантажити на пряму, але не можна замінити єдиний файл — тільки все разом. На AWS S3 можна просто оновити окремі файли.
Нарешті, хоч і здається, що статичний хостинг — найшвидше, що може бути в вебі, насправді провайдери, особливо безплатні, не гарантують швидкості. В мене Ahrefs не раз скаржився на повільні сторінки на Vercel. А найшвидше сайт працював на власному сервері — але ж то незрівнянно складніше в підтримці. А ще, на власному сервері буде максимальна кількість особливих умов, та з нього найскладніше буде зʼїхати.
Хотілося закінчити на рекомендації конкретного хостингу, але її в мене немає. Хіба можу порадити не вигадувати власні обмеження (як я зі слешами), а триматися ближче до базової парадигми “сторінка = файл”.