Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS · 📢 Канал в Telegram @stendap_sogodni
27.10.2024
Проєкти у GTD
В GTD ментальна модель роботи ділиться на: наступні дії та проєкти. Та якщо наступні дії це штука більш-менш схожа на будь-який список задач (принаймні, вдалий), то проєкти можна розуміти дуже по-різному.
На мою думку, ідея проєкту спотворена спробами їх формалізувати. Вкладені проєкти, послідовні проєкти, паралельні проєкти, проєкти на паузі… Будь-яка сучасна програма для керування задачами заганяє проєкти в надто вузькі рамки.
Та, на відміну від наступних дій, проєкт в GTD має широке значення. Та жодного натяку на структуру. Проєкт — це будь-яка мета, до якої ми зараз йдемо. Оскільки майже нічого не здобувається за одну наступну дію, нам потрібно памʼятати, чого ж ми плануємо досягти. Сенс списку проєктів — щоб не тримати всі мети в голові. (Бо весь сенс GTD - це не тримати нічого в голові.)
В GTD ніякої структури в проєктів немає, це простий список. Єдине, що кожному проєкту повинна відповідати хоч одна наступна дія. Що й значить, що ми зобов’язуємося робити хоч якийсь прогрес. Дій може бути й декілька — якщо вони не взаємозалежні. Так регулярно трапляється з великими проєктами.
Бо проєкт може бути великим чи маленьким — таким, щоб зручно було про нього думати. В голові мети “день народження”, “купити будинок” та “виправити баг” уживаються, тож в списку проєктів теж. З іншого боку, якщо від “ремонт” відпупковується “знайти меблі”, то проєктів стає два. Знов-таки, все це робиться, щоб нам не потрібно було тримати речі в голові, а не заради утворення якоїсь загальної моделі життя.
А чого в списку бути не повинно, так це проєктів, які ми зараз не робимо. Такі зʼявляються постійно, та під час тижневого огляду їх можна помітити за браком активності та видалити. Інколи проєктів стає забагато та на всі бракує часу, а інколи проєкт просто дурний та зайвий. До речі, що мені дивно — в GTD список проєктів взагалі переглядається тільки під час тижневого огляду, тобто раз на тиждень — певно, протягом тижня вистачає списку дій. Що знов-таки дуже відрізняється від сучасних програм, де і дії, і проєкти зведені в один список.
26.10.2024
Середовище розробки для Hugo
В мене блог на Hugo та в цілому для мене як для інженера, та ще й з досвідом Go, не маю кращих варіантів. Hugo вміє все, включаючи самостійно збирати JS/CSS та обробляти зображення. Попри всю цю потужність, в мене гальмувала робота над оновленням сайту. Поміркував та зрозумів, що це через брак зручного середовища розробки.
Hugo використовує шаблони Go - звичайні html/template. Акуратний HTML і так не легко підтримувати, а коли в нього домішують теги шаблонізатора, то взагалі. (Або це я зіпсований роками роботи з JSX, який форматується з математичною точністю.)
Втім, все вирішується гарними інструментами. Дізнався, що тепер в Prettier є плагін prettier-plugin-go-template, який зокрема вміє форматувати й шаблони Hugo. Цікаво, що для того шаблон повинен мати “збалансовану” вкладеність, тобто, наприклад не можна робити {{if .x}}</div><div>{{end}}
. А Prettier в мене вже був, для TypeScript та для Markdown - тепер розширив його обовʼязки.
Далі, певно, потрібно не забувати про стандартні засоби боротьби зі складністю: розділяти код на файли, узагальнювати повторювані місця. З одного боку, шаблони Go все це вміють, а з іншого, в Hugo вже й так складна система вибору шаблону — хоч здається, що типів сторінок не так багато, але є ще “архетипи” постів, та для кожного з них різні сторінки переліків, а ще є сторінки категорій та тегів… і все це повинно працювати синергійно. Принаймні сервер розробки дійсно зручний та навіть перезавантажує сторінки автоматично.
Ще є скрипти, що виконуються на сервері. Звісно, це вже не Hugo, бо він тільки робить статичний сайт — а Vercel, Netlify, Cloudflare Workers чи що там у вас. Але локально сервер розробки вміє інтегрувати їх в сайт десь так само, як і хостинг. Для того є конфігурація redirects; звісно, сам сервер зі скриптами доведеться запускати окремо, може, утилітою goreman.
25.10.2024
Kafka: неочевидне з досвіду
Неочевидна сильна сторона Kafka: кожний потік можна обробляти будь скільки разів. Тобто я звик думати, що черга це: один зайшов — один вийшов. А з Кафкою не так. В Кафці записи живуть визначений час — наприклад, декілька днів — та протягом цього часу доступні для читання скільки завгодно разів. Причому якщо під час читання оголосити ідентифікатор — тобто створити групу споживачів (consumer group) - то Кафка відстежуватиме її позицію в потоці записів.
Тобто з Kafka маємо незнайому парадигму, де головною сутністю є не окремі записи, а їхня послідовність — тема — яку ми можемо наповнювати та споживати, як це потрібно. Наприклад, ми можемо використовувати послідовність подій як джерело правди для нашої бази даних, але також сканувати ту ж саму послідовність на присутність цікавих нам маркерів, а ще обчислювати за ними статистику в реальному часі та записувати в іншу тему ще для чогось.
Неочевидна слабка сторона Kafka: відсутня можливість відкласти запис на майбутнє. Для черги це типова функція, але ж Кафка це не черга (а журнал!) Одна з головних характеристик Кафки — записи читаються в тому самому порядку, як їх записали. Так само й статус прочитання призначається послідовно. А щоб відкласти на майбутнє, нам потрібно порушити цей порядок.
Тобто вбудованого механізму немає, але ж можна придумати свій. Наприклад, є потреба повторно обробляти невдалі записи — та відомо, що кожна наступна повторна спроба буде пізніше за попередні. Тоді можна зробити окрему тему для повторних спроб, а також процес, який вичитує наступну спробу, чекає потрібний час, та надсилає назад в головну тему.
А от можливість відкласти отримання на невизначений проміжок взагалі не вкладається в модель Кафки, та краще вже брати нормальну чергу.
24.10.2024
Маленькі кроки
Останнім часом (як почав підтримувати список наступних дій на роботі) практикую такий підхід до їхнього виконання: беру першу дію, виконую, потім другу… і так до кінця. А нові дії додаю в кінець.
Цей підхід гарантує, що нічого не залежується в списку. Але є очевидна проблема: час від часу стикаєшся з дією, яка надто складна або незрозуміла, та застрягаєш. Так само і якщо робити задачі не по черзі — з тією різницею що такі задачі можна перестрибнути, тому вони зазвичай застрягають надовго. А в мене як раз багато задач, котрі складні, незрозумілі, але все ж вимагають розвʼязків.
Тоді я роблю собі поступку та намагаюсь зробити хоч крок до розвʼязку. Прибрати застарілий код. Розставити коментарі. Написати тест — але не для нового, а для старого коду. Розрізати незрозумілий клас на два. Перейменувати функцію. Метою таких кроків є розуміння задачі: тобто те, чого нам бракує. Так покопирсаєшся, покопирсаєшся та стане ясно, що робити далі.
В цьому для мене і є секрет розвʼязання складних архітектурних задач. Не знати все — а вміти відтяти від великого та незрозумілого маленьке та досяжне.
23.10.2024
Програмний доступ до Docker
🐳 Чи знаєте ви, що в Docker є зручний клієнт на Go? Інтеграційний тест, про який я два дні тому писав, зупиняє контейнер саме через цей клієнт.
Причому якщо вам не потрібна авторизація, щоб запускати консольні команди docker
, то й тут теж не потрібна: client.NewClientWithOpts(client.FromEnv)
буде достатньо. Але авторизуватися та керувати іншими машинами теж можливо.
Ми використовували дві функції клієнта: вище згадану зупинку контейнера, а також отримання журналу через client.ContainerLogs()
. Через журнал перевірялося, що в ньому немає помилок. Окрім того, буквально все, що можна зробити з Docker, можна зробити через цей API - бо сам Docker написаний на Go та його офіційні клієнти теж.
Та ось що дотепно — мені такий інтерфейс набагато більше подобається, ніж стандартний командний рядок! Він весь типізований та тому можливості перелічені та вичерпні. Наприклад, ось тип Mount - всі атрибути явні, навіть з переліченими типами на кшталт Type
чи Consistency
.
Можна було б взяти оцей модуль та зробити та його основі (та на основі самого Docker) щось цікаве — може, сервіс з пісочницею, може диригента для складних інтеграційних тестів.
22.10.2024
Rails, Asset Pipeline, та артефакти
Якось в розмові згадалося, що Ruby on Rails не дуже дружня до збірки на CI. Принаймні, історично. Хоча й зараз офіційна документація згадує якісь хаки, тому те, що я розповім, більш-менш актуально й досі.
Коли я почав працювати з Rails, то “збірки JavaScript” як такої не існувало. Та й JavaScript багато не було. Шматочки JS та CSS просто додавали в теку public
. Не тільки в Rails, а взагалі. А вебзастосунки розгортували копіюванням файлів на сервер.
Коли у 2011 в Rails зʼявився механізм для збірки - Asset Pipeline - то не було ані Webpack, ні навіть Node.JS в широкому використанні. Памʼятаю, складно було взагалі отримати інтерпретатор JavaScript не в браузері. Тому логічно, що збірка була частиною застосунку, а саме Rake-скриптом. Цей скрипт запускався вже на продакшні, щоб підготувати JS/CSS. Ніхто його не ізольовував від решти застосунку, тому, наприклад, збірка мала доступ до бази. (Я вам навіть скажу, десь в мене був такий код, що витягав з живої бази даних якісь словники та генерував на їхній основі JS.)
Спочатку збірка JS відбувалася на Ruby та за власним для Rails алгоритмом. Але потім в JS виросла власна екосистема, з пакетами, інструментами та так далі. Якийсь час було заведено збирати Webpack окремо, а в Rails тільки зчитувати його маніфест — на то існував пакет webpack-rails. Це було досить чисто, та дозволяло відʼєднати збірку JS/CSS, передавши в застосунок тільки маніфест.
Але у 2019 мода перейшла до webpacker, який знову втягнув Webpack всередину застосунку. Як на мене, то був наслідок поширеної серед рубістів догми “рубі найкращий, давайте все в нього загорнемо”. Та нам знову стало потрібне середовище застосунку, щоб зібрати JS/CSS. Десь в той самий час розгортування переїхало на CI. Проблема з середовищем застосунку в тому, що на CI його треба затикати, та порожня база — це тільки початок. По коду доводиться розставляти перешкоди: “якщо ми у збірці, то сюди не йдемо”. Нові залежності підносять сюрпризи.
Сучасний підхід - jsbundling-rails - надав нам вибір між інструментами збірки, але все ж залишив її у контексті застосунку Rails. Тож нам все одно потрібно середовище застосунку на CI. Та, мабуть, найголовніше що мене дратує — що залишається та давня парадигма, що ми збираємо JS/CSS саме для цієї копії застосунку. Ніби це ми його тільки що залили по SCP на наш VPS, як у 2010-му. Та коли стає питання таку збірку робити артефактом, то мені від того трохи ніяковіє.
21.10.2024
Найскладніший інтеграційний тест
Є тут така ситуація… Сервіс під час своєї зупинки робить рукостискання наступнику. Причому наступника знаходить через AWS API. Причому рукостискання передає контекст, в тому числі зібраний зі чужої бібліотеки. От, коли чужа бібліотека після оновлення несподівано змінила порядок дій, поламала весь процес, та помітили це тільки в продакшні, я вирішив — пора зробити інтеграційний тест!
Таку історію й перевірити вручну не так легко, бо це потрібно не тільки розгорнути версію для перевірки, а два рази її розгорнути — щоб новий код зупинки виконався. На це ні в кого немає часу! Тому автоматичний тест піднесе надійність на новий рівень. Але як його реалізувати?
Першим чином прибрав залежність від AWS. В цій частині я впевнений, до того ж перевіряти її мало сенс тільки зі справжнім AWS. Зробив механізм пошуку наступника модульним.
Планував запускати локально дві копії сервісу та перевіряти передачу контексту між ними. Але в тому коді, що його приймає, я теж впевнений — бо його легко перевірити в ізоляції. Тому натомість написав маленький тестовий сервер, який й стане “наступником” в тестовому середовищі. Все, що від нього потрібно — це прийняти запит та зберегти його для перевірки.
А ось роботу з чужими бібліотеками та зупинку точно потрібно робити в інтеграції, бо саме тут виникають помилки, які важко помітити. Тому її залишив без змін. На щастя, зупинити сервіс, запущений в Docker, нескладно.
Завдяки відтинанню зайвого вийшов цілком практичний тест — якщо запускати його останнім, то ще й на зупинці сервісу можна заощадити!
20.10.2024
Оцінювання задач
Я як розробник не люблю детально оцінювати роботу в часі. Зокрема тому, що в мене практично завжди стоїть декілька паралельних задач, плюс підтримка, плюс допомога. В таких обставинах оцінки не складаються. Виходить, найточніша оцінка — це чи я встигну зробити задачу на цій ітерації. І це більше обіцянка-зобовʼязання, ніж оцінка.
З іншого боку, в оцінювання є надважливий побічний ефект: воно примушує розглянути задачу ближче. Розбити її на кроки. Помітити брак інформації або рішень. Це, може, й не необхідно для виконання, але значно його спростить.
У нас наразі є нарада “для збагачення задач”. Оцінюванням ми там не займаємось, натомість командою обговорюємо все наплановане та висвітлюємо незрозумілі моменти. Також інколи розкладаємо на підзадачі. Мабуть, моя улюблена нарада. Зовсім не сумую за дебатами “це задача на 2 бали або на 3”.
В оцінювання є й інший бік: для ефективного керування потрібно знати, скільки займе та чи інша робота, щоб координувати її з іншими командами та планами. Так от, я колись працював на клієнта, в якого релізи були жорстко та беззастережно привʼязані до календаря. Там я на практиці побачив закон Паркінсона: робота заповнює весь відведений час. Звісно, ніколи не було такого, щоб графік був вільний — навпаки, майже кожний реліз був спішним. Але з тим, кожний реліз відбувався, в цілком робочому стані.
Дехто сприймає закон Паркінсона, як наслідок ліні, але я з цим не згодний, принаймні для нашої роботи. Програмування — творча діяльність; в ній завжди є що доробити… або залишити не доробленим.
Тому я вважаю, що для високорівневого планування розробки ПЗ достатньо мати вірні приблизні оцінки — може, в місяцях — та ставити за мету дотримуватись цих термінів. Але! Щоб вірно оцінити роботу навіть в місяцях, без деталізації не обійтися. Можна це назвати… оцінюванням з одного балу.
19.10.2024
Що таке — забагато задач?
🐢🐇 Трохи суботнього керування часом.
Минулого разу я писав про завчасну обробку вхідних. Це чудовий спосіб не відставати від життя, та мені дуже подобається починати кожний день з “прибирання”. Але тут є нюанс: з вхідних я створюю проєкти та дії — робота над ними тільки починається. Та що веселіше обробляєш вхідні, то довше ростуть списки дій.
Намагаюся зараз допомогти собі побачити, коли дій стає забагато. Метою моєю є закривати кожну дію протягом тижня. Пізніші дії можуть почекати в проєктних планах, але додавання дії в список наступних повинно значити, що я її скоро зроблю — тільки так системі можна довіряти.
З цього вже корисно відокремити дії, старіші за тиждень. Але такий індикатор зʼявляється запізно. Краще щоб метрика зростала від 0, коли всі дії нові, до 1, коли хоч одна з них “прострочена”. А ще краще знати, що дій забагато в той момент, коли хочеш додати нову: бо тоді її можна відразу відкласти на потім. Поки я в пошуках гарної функції.
PS. В Agile часто використовується поняття швидкості (velocity.) Це як раз кількість задач, які команда закриває за ітерацію. Я його і для роботи не люблю (та ми й не використовуємо): на корисний вимір швидкості бракує педантичності. А для особистих задач тим паче: обсяг задач та можливість їх робити надто мінливі.
18.10.2024
Інструкції та розуміння
Часто є два способи пояснити шлях до розвʼязку. Можна зробити інструкцію, тобто послідовність кроків. А можна передати розуміння, тобто базові принципи. Інструкції специфічні. Розуміння загальне. Інструкції легше виконати, особливо поки вони не втратили актуальність. Розуміння набути важче, зате потім будеш готовий до всього.
Я зрозумів це на прикладі ферментації. В інтернеті багато інструкцій про те, як закривати посуд для ферментації — зазвичай або спеціальною кришкою з клапаном, або звичайною, але регулярно випускати гази. Складається враження, що нам потрібно: 1) випускати гази — щоб не рвонуло — та 2) захищати посуд від комах, бруду та іншого. Тобто якщо кришки з клапаном немає, а відкривати лінь, то можна бути розумним, прикрити кришкою, але не щільно — та питання закрите? Ні. Це враження хибне. Насправді 3)нам вкрай важливо створити середовище без кисню, тоді на продукті не зʼявиться пліснява. (Також важливо, щоб продукт був кислим — для захисту від ботулінової палички — але це вирішує сама ферментація.) Це вже розуміння, та коли воно є, можна знайти вірну кришку з наявних.
Інструкції, як щось конкретне, писати легше. Навіть не треба самому мати це розуміння — достатньо перелічити власні дії. Тому в проєктах багато інструкцій та менше пояснень, що та навіщо вони роблять. Та тому коли розуміння зʼявляється (часто в той момент, коли попередня інструкція не спрацювала), то обовʼязково треба його записати (часто як коментарі до нової інструкції.)