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

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

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

18.11.2022

Cкладнощі з тестуванням БД на Go

🐹🗄️😭 Поскаржуся на складнощі з тестуванням БД на Go.

Попередження: мені ще не доводилося використати жодну з повноцінних ORM для Go. Причина в тому, що в мене завжди десь поруч Rails, а у Rails чудовий інструментарій для міграцій і такого іншого. Тож на долю Go залишається тільки обмежена кількість запитів. Тож записуємо їх явно, у вигляді SQL, а запускаємо за допомогою database/sql. Але ж їх теж треба тестувати.

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

Спочатку треба відтворити структуру бази для тестів. Якщо є Rails, то це просто — беремо базу від Rails. Також для CI копіюємо database_structure.sql з Rails в репозиторій з Go, і завантажуємо перед тестами. Теж нескладно (тільки доводиться вручну підтримувати в актуальному стані.)

А далі — складна частина. Як наповнити тестову базу даними? В Go немає загально прийнятного аналогу factory_bot - принаймні, такого, що можна вжити без ORM.

Раніше я наповнював базу довжелезними SQL-скриптами - на кшталт TRUNCATE ...; INSERT .... Їх жахливо читати та змінювати. До того ж в скрипті незрозуміло, де важливі значення, а де — необхідне наповнення обовʼязкових стовпчиків. Втім, саме такий підхід я вживав багато років на декількох проєктах — бо принаймні він технічно простий.

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

Бібліотека testfixtures дозволяє описати таблиці у вигляді YAML. В цих файлах YAML можна навіть використати шаблони, та передавати значення з коду Go. Тобто замість непрозорих, магічних значень тепер в тестах можна посилатись на ті ж самі змінні, що будуть записані в базу. Чого не вистачає — автоматичного наповнювання обовʼязкових стовпчиків.

Так поки й живемо. А ще налаштування бази зручно робити з testify/suite.


17.11.2022

Система пошуку в інтернеті Kagi - мій новий пошукач

🔍🦮🎩 Сьогодні несподівано поміняв свій підхід до пошуку в інтернеті.

Я вже багато років вживав як базовий пошук DuckDuckGo. Для мене головна його перевага — це так звані bang-команди, що дозволяють відразу делегувати пошук на інший сайт — наприклад, !g foo шукатиме в Google, а !yt bar - на YouTube. Сам по собі пошук у DuckDuckGo кепський, бо власного індексу він не має, а шукає через Bing. (Тому мого блогу в DDG не знайти.) Але, можливість легко перестрибнути в більш доцільний пошукач мене полонила.

Але. Колега порадив пошукач “нового покоління” Kagi. За який треба платити $10 на місяць. Що ж такого він пропонує за ці гроші? Вочевидь, гроші за підписку — гарантія (або обіцянка) того, що пошукач не торгуватиме твоїми даними. (А ще спробувати до 50 запитів можна без оплати.)

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

Як щодо bang-команд, то Kagi їх підтримує. Але я вирішив піти ще далі, та знайти локальну заміну. Бо як DDG, так і Kagi перенаправляють bang-команду зі свого сервера, а локальна утиліта піде напряму. Так я знайшов доповнення для Safari xSearch - це буквально те що треба. Працюватиме xSearch і на айфоні.

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

Отже, я вирішив піти до кінця та залучити додаток, що в мене вже є - Alfred. Єдине що, треба перевчитись писати пошукові запити туди, а не в браузер.


16.11.2022

Obsidian, погана синхронізація через iCloud Drive, та хороша через Obsidian Sync

🪨☁️🔄 Obsidian розчарував з синхронізацією через iCloud Drive, але з цього можна винести гарний урок.

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

Одним словом, для синхронізації iCloud Drive погано підходить. Як хмарне сховище для файлів — добре, але якщо потрібна швидкодія — ні.

Тому довелось заплатити гроші та перейти на Obsidian Sync - їхній власний сервіс. Має end-to-end шифрування, тобто на сервері нотатки прочитати неможливо. Нотатки, що синхронізуються з Obsidian Sync, розташовуються у звичайній локальній директорії, та ніякої неоднозначності з їх наявністю немає. Як на телефоні, так і на компʼютері пошук працює швидше.

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


15.11.2022

Як підтримувати швидкодію додатків на React

⚛️🚅📝 Зачепив сьогодні тему того, як підтримувати швидкодію додатків, написаних на React.

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

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

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

Redux вносить другий вимір у рівняння швидкодії. Кожний з селекторів — тобто функцій, які використовуються в хуці useSelector - буде виконаний при кожній зміні стану. Щоб уникнути проблем, варто не привʼязувати селектори до компонент, що можуть повторюватись багато разів.


14.11.2022

Перетворення коду за допомогою синтаксичного дерева AST

🔠✂️🌳 Хочу детальніше розповісти про механізми, що дозволяють, наприклад, написати перетворювач Markdown на HTML для Телеграму, не розбираючись при цьому з синтаксисом Markdown.

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

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

Саме так працює Goldmark - парсер Markdown для Go. Окрім того, щоб писати власний рендерер, можна було також написати трансформацію дерева, а потім передати звичайному рендереру. Так, наприклад, можна додати особливу розмітку, чи навпаки, обмежити її.

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

А найбільш відоме всім нам синтаксичне дерево — це, напевно, дерево HTML, тобто DOM.

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


13.11.2022

Бібліотека для форматування Markdown для публікації у Telegram

📢🤖🏆 Почав опен-сорсити свого бота для Телеграм. Першим пішов модуль для форматування записів, який буде корисним сам по собі.

Назвав його Telegold, бо в Go треба мати коротке імʼя, а якщо вже довге — то розбивати його слешами, а ніяк не рисочками.

Він стане до нагоди, якщо будете писати бота, що публікує будь-який користувацький контент у форматі Markdown. Технічно, можна вже брати (власне, мій бот так вже й робить), але тестів бракує.


12.11.2022

Мій найуспішніший Open-Source проєкт - Headless

🎃🏇🖥️ Розповім про свій найуспішніший та, можливо, найдурніший Open-Source проєкт, а саме бібліотеку для Ruby під назвою Headless. З 11 мільйонами завантажувань вона всього в 35 разів менше за популярністю від Rails.

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

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

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


11.11.2022

HealthKit та історичний графік ваги

⚖️❤️💹 Сьогодні продовжив працювати над проєктом для HealthKit. А саме, зробив базову версію історичного графіка ваги — за декілька місяців.

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

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

Також дізнався, що у Swift все ж таки є модуль для роботи з календарними датами, називається Calendar. Цікава абстракція — дати (Date) відтворюють моменти часу, але модуль Calendar дозволяє будувати та змінювати дати згідно з правилами календаря.

До речі, оскільки нещодавно ми перейшли на зимовий час, я вже встиг отримати баг через те, що додавав дні до дати як 86400 секунд, а день переводу часу був на 1 годину коротше. Тому на графіку зʼявилася некрасива нерегулярність. Використання модуля Calendar все виправило.


10.11.2022

Оголошення гему actionmailer-balancer

💌🚚🧑‍⚖️ Сьогодні анонс гему, який ми зробили для комфортабельної зміни провайдера електронного листування. Це actionmailer-balancer.

Якщо ви серйозно займаєтесь надсиланням електронної пошти, то репутація вашого домену — це суттєвий ресурс. Репутація — то базовий рівень довіри поштових сервісів. Чим вона краще, тим менше шансів, що ваші листи підуть у спам, або ще гірше — не будуть прийняті взагалі.

Репутація будується посиланням “хорошої” пошти, та униканням “поганої”. Погана пошта — то, очевидно, спам, але ж часто цілком нормальні листи отримують “погану” оцінку через всілякі технічні проблеми — відсутність підпису DKIM, репутацію IP адреси та інше.

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

Вихід такий — переносити пошту на нового провайдера поступово, починаючи з незначної долі загального обсягу. Але як це зробити технічно? Для цього ми й зробили гем, що дозволить розділити потік пошти з додатка Ruby on Rails на два або декілька провайдерів. Завдяки гему, поступовий перехід здійснюється так же легко, як і перехід цілком.

(Ще actionmailer-balancer можна залучити для балансування декількох безплатних планів, щоб надсилати більший обсяг пошти, ніж це дозволить один з них. Але це іграшкова ситуація, як на мене.)


09.11.2022

Як поєднати Ruby on Rails та AWS Redshift

☁️🗄️☑️ Коротенький чекліст, як зробити, щоб Rails і Redshift запрацювали гармонічно:

… Тож насправді чекліст виходить не такий і короткий.