Стендап Сьогодні
Що я зробив, що я хочу зробити, і що це все значить.
Повсякденні здобутки в форматі стендапу.
Детальніше в статті
Підписатись на RSS
📢
Канал в Telegram @stendap_sogodni
🦣
@stendap_sogodni@shevtsov.me в Федиверсі
25.03.2023
Cюжетні пазли - мало відомий піджанр детективних ігор
🕵️🧩🎮 Є такий мало відомий піджанр детективних ігор, я б назвав його “сюжетний пазл”. В таких іграх всі події сюжету вже відбулися в минулому, а нам залишається тільки розслідувати їх. Метою є зібрати з розрізнених шматочків певну картину подій. Це нагадує такі фільми, як Памʼятай або Підозрілі особи. Або процес пошуку помилки в логах. :)
Ось декілька “сюжетних пазлів”, які мені дуже сподобались. Одне в них погано — по другому разу проходити вже не цікаво.
Her Story - вся гра — це архів інтервʼю з дівчиною, який можна шукати за ключовими словами. Треба зрозуміти, що з нею сталося. Деякі ключові слова можна просто вгадати, а деякі доведеться виділяти з вже доступних оповідей. Найприємніше, коли помічаєш особливе слово, за яким розкривається відразу цілий шар небачених досі відео.
Return of the Obra Dinn - розгадай, як помер кожний член команди корабля-привида, та як їх звали. Причому вся інформація, яка є — це момент смерті для кожного знайденого (по черзі) тіла. Причину смерті здогадатись нескладно, а от де хто, доведеться розбиратись за неявними ознаками. Гра має унікальний графічний стиль та чудову озвучку.
Eternal Threads - треба побудувати (а точніше, відновити) дерево рішень, яке призведе до правильного результату. Причому це не так просто, як вибрати “правильне” рішення кожного разу! Бо треба ще обрати правильний момент, а також інколи “помилятись”, щоб вчитись на цих помилках. Та, звичайно, з кожним рішенням відкриваються або закриваються гілки дерева…
Ще у мене в списку бажань The Case of the Golden Idol та Telling Lies.
24.03.2023
Коли закладати локалізацію додатка?
Обговорення вчорашньої статті на DOU порушило цікаве питання. А саме, чи має локалізація додатка бути закладена на початку розробки? Я маю на увазі — чи треба від початку роботи складати рядки у словник та використати в коді ключі перекладу замість рядків без перекладу?
На моєму досвіді, щоб це мало сенс, треба дві умови. Перше — щоб інтерфейс додатка був чітко заданий заздалегідь. Якщо розробка має, так би мовити, характер Agile, та екрани змінюються з новими вимогами та випробуваннями, то ви змарнуєте купу часу на підтримку словника для фраз, які не попадуть до остаточної версії.
Друге — що багатомовність має бути обґрунтована потребами бізнесу. Та головне, що на перекладі додатка інтернаціоналізація тільки починається. Треба ще продавати його на різних ринках, робити розкрутку, рекламу, підтримку користувачів. Перекладати інформаційні матеріали, нарешті. Якщо бізнес не планує це робити, то можливість зміни мови в додатку має суто символічний характер.
Я вважаю, що такий збіг обставин трапляється рідко. Так, якщо робити сервіс мікрокредитів для європейського банку, то в них і дизайн готовий є, і перелік країн, де він буде представлений. В інших випадках раннє впровадження локалізації тільки вадить розробці. Як я показав, додати i18n пізніше — цілком реально, а якщо це так, то не бачу сенсу думати про інтернаціоналізацію раніше, ніж це буде потрібно.
Навіть при розробці нового функціоналу в додатку, що вже має i18n, я схиляюсь до того, щоб нові рядки локалізувати вже наприкінці іншої розробки. До того ж як я писав в статті, в деяких місцях i18n здатний суттєво ускладнити архітектуру. Доведеться думати не тільки про розвʼязання базових задач бізнесу, а ще й про те, як розвʼязок локалізувати. Як на мене, то простіше ці дві справи робити окремо.
23.03.2023
Поштовий заголовок Received
Продовжуючи серію про приколи електронної пошти, розповім про заголовок Received.
Це один з тих заголовків, що виставляються поштовою інфраструктурою, а не клієнтом, тому звичайні відправники про нього знати не будуть. Хоча, якщо відправляєш напряму до отримувачів — листи без заголовка Received мають високий ризик попасти в спам. На перший погляд, це дивно — хіба заголовок з назвою “Отримав” виставляє не отримувач? Чому відправник має про нього думати?
А от виходить, що Received - це як штемпель поштового відділення (застаріла аналогія у вік Нової Пошти.) Кожний сервер, через який проходить лист, має додавати свій Received, в якому відстежується шлях листа. Зазвичай листи від відправника не йдуть прямо до отримувача, а проходять через так званий message submission agent. Це як ваше поштове відділення. Він й має виставити перший заголовок Received.
Для звичайних, людських листів MSA - це той поштовий сервіс, яким ти користуєшся - GMail, наприклад. А якщо вже відправляти пошту з додатка самотужки, без поштового сервісу, то треба не забути самому додати Received, в якому, як мінімум, буде зазначена дата посилання:
Received: from myapp.io by mx.myapp.io; Thu, Mar 23 2023 23 11:27:16 GMT
Все це описано у RFC 822.Через такі особливості SMTP і варто доручити доставлення пошти спеціалізованому сервісу, такому як Mailtrap - нехай вони (тобто ми :) самі читають RFC.
Також сьогодні на DOU вийшла моя стаття про українізацію Сінтри. Частково я ділився деталями тут на каналі, ще минулого літа, але тепер можна прочитати все разом.
22.03.2023
Як довести думку через ланцюг міркувань
Як довести думку до аудиторії? Це може бути потрібно як коли даєш презентацію, так і повсякденно — коли треба пояснити колегам рішення чи ситуацію, яка склалась. Тобто навичка ця корисна до кожного, хто працює в команді.
Я використовую таку модель. Треба визначитись з кінцевою думкою. Потім взяти, явно чи не явно, базовий рівень знань аудиторії. А потім — побудувати ланцюжок думок, який починається з чогось загально доступного для аудиторії, та закінчується висновком.
Найскладніше в цьому процесі — це уникнути стрибків в міркуваннях. Це коли наступна думка не випливає з попередньої. Звичайна причина стрибків — наші переконання, часто приховані. Через ці переконання для нас все послідовно — переконання заповнюють розрив в логіці. Але аудиторію такий стрибок примушує самим шукати пояснення, що або відвертає увагу, або взагалі викликає недовіру до презентації.
Тому я багато разів проходжу по тексту та перевіряю — чи не пропустив я крок. Якщо знаходжу — намагаюсь розкрити свої переконання. Насправді може це і є найцікавіше в презентаціях — досвід. з якого автор має ту чи іншу думку.
Як приклад, моя презентація про підхід React виросла навіть не з думки, а з почуття, яке раніше було описане в статті. Але щоб це почуття викласти, довелося довго копатися у своїх попередніх досвідах розробки, та в першу чергу для себе його зрозуміти. (Ось слайди, до речі.)
Буває й так, що ланцюжок ніяк не будується. Тоді доводиться починати спочатку. Або навіть передивитись свою кінцеву думку, бо вона виявилась хибною чи необґрунтованою — таке теж трапляється.
21.03.2023
Чи боятись Vendor Lock
Мене сьогодні спитали, чи не страшно спиратись на AWS Redshift, дивлячись на Vendor Lock, тобто привʼязку до постачальника. Моя коротка відповідь — привʼязки до постачальника тут немає. Чому, та коли це є проблемою — нижче.
Для мене привʼязка до постачальника — в контексті програмної архітектури — значить, що продукт буде неможливо в майбутньому перенести на іншого постачальника без повного його перетворення. Як приклад — якщо у вас є додаток на iOS, то ви привʼязані до iOS; без iOS додаток не може існувати.
Трохи слабший приклад — Сінтра побудована на Google Firebase; це настільки “не просто” база даних, що якщо ми вирішимо переїхати ще кудись, доведеться заново зробити всю архітектуру додатка, окрім його верхнього шару з бізнес-логікою. Знайти, як робити живе оновлення, авторизацію, і так далі. Проте, Сінтра це переживе. Головне, що ми вільні забрати свої дані — неможливість експорту даних це як не найгірший випадок vendor lock. (А ще, користувач Сінтри теж вільний забрати свої дані, та своєю чергою не є привʼязаним до нас!) До речі, існують відкриті сервіси-замінники Firebase такі, як Supabase, за якими я стежу на всяк випадок. Але поки ми зовсім не збираємось кидати Firebase та ради користуватися її дивовижними функціями та не витрачати час на їх розробку та підтримку.
Якщо брати технології AWS, то серед них є такі, що схожі по незамінності на Firebase. Це, на мою думку, база AWS DynamoDB, або процесор подій Kinesis Firehose. Вони настільки специфічні, що для переїзду на інший сервіс доведеться переосмислити вашу архітектуру (хоча, он, у DynamoDB є замінник в Azure - не знаю, наскільки хороший.)
А інші сервіси AWS, хоч і є специфічними, але суттєвої різниці не пропонують. Так, хостинг контейнерів ECS не дуже складно замінити на будь-яких хостинг на основі Docker. До цієї категорії я відношу і Redshift - він є типовою базою категорії Data Warehouse, а в теорії, з Redshift можна переїхати хоч на звичайний PostgreSQL.
Але взагалі, перше, що мене турбує — це які задачі вирішує сервіс, та наскільки він спростить розробку для нашої бізнес-задачі. А потім вже про ризик vendor lock - чи надійний постачальник, які витрати обіцяє в майбутньому, наскільки сильна привʼязка. Та може виявитись — як у нас з Firebase - що навіть сильний vendor lock буде цілком виправданим.
20.03.2023
Складнощі з міграціями в Redshift
Найбільш за все у схемі перетворення даних розрізами в Redshift мене дратує, що зміни структури бази ускладнюються суттєво.
В Redshift обмежені можливі зміни стовпчиків. Навіть така незавадна зміна, як з NOT NULL на NULL, не дозволена. Можна тільки додати новий стовпчик з потрібним типом та перенести туди дані. На щастя, стовпчики можна перейменувати, тому принаймні можна замінити старий стовпчик на новий. Поки в нас не було розрізів, все було більш-менш прийнятне.
Але якщо від стовпчика залежить розріз, то такий трюк не вийде. Тоді залишається тільки видалити розріз, змігрувати таблицю, та створити розріз наново. (А якщо розрізів багато… Доведеться перестворювати всі.)
Але так теж не вийде, якщо розріз вже активно використовується. Хоча б тому, що перше створення розрізу може зайняти багато часу — часу простою. Тоді доведеться створити новий розріз, може, з версією - my_stats_v2_mv, наповнити його даними, та потім плавно перевести код зі старого на новий розріз. Перейменувати розріз не дозволено, тож він назавжди залишиться з версією в назві. Тобто з часом буде і v3_mv, і так далі, на кожну необхідну зміну.
Добре, що всі ці складнощі можна частково приховати за красивим кодом з боку додатка. Є навіть гем Scenic, який вміє версіонувати розрізи. Тільки Redshift він не підтримує.
19.03.2023
Форми в React
Сьогодні, нарешті, повернувся до Сінтри, на яку останніми місяцями ніяк не вистачає часу. Працюю над декількома складними формами.
Для створення форм у React я беру react-final-form. (Є ще formik, який дуже схожий за використанням.) Головне що така бібліотека заміняє купу копіпасти, яка потрібна для керування станом полів окремо. Але взагалі, абстракція форми в React та в HTML трохи різна, та якщо це розуміти, форми покриють ще більше потреб.
Чого точно не треба робити, це зберігати стан форм в Redux. Будь-яка зміна стану Redux призводить до виклику кожного селектора в додатку. Тільки так Redux може визначити, які компоненти потребують перемальовування. Відповідно, якщо на кожний символ, введений в форму, міняти стан Redux, то втрата швидкодії забезпечена та залежить тільки від розміру додатка. Причому це фундаментальна проблема Redux; ніколи не кладіть в Redux те, що змінюється щосекунди або частіше. Прощавайте, фантазії про “чисту архітектуру”. (До речі, дуже ціню, що React Final Form дозволяє звузити підписку на зміни, та уникнути зайвої перемальовки. Так мені вдалося зробити навіть маленький табличний редактор, цілком на можливостях бібліотеки.)
Тоді виходить, що бібліотека react-final-form - це, в першу чергу, механізм збереження стану. Стан цей тимчасовий, та існує від початку змін до збереження їх в стале сховище. Це не завжди короткий проміжок; наприклад, в Сінтрі весь майстер створення бюджету з чотирма сторінками — єдина величезна форма. Так ми легко маємо доступ до будь-якого поля з будь-якого місця форми, а також можемо гортати майстер вперед та назад. Тільки коли ти натискаєш “Створити бюджет”, в базі даних створюються необхідні записи. До речі, щоб не втрати стан форми при перезавантаженні сторінки, дублюємо його в LocalStorage.
Одним слово, що я хочу сказати, що в React форма зовсім не має виглядати як форма, а натомість є абстракцією, яка спрощує керування тимчасовим станом деякого процесу.
18.03.2023
Відновлення флешки з APFS, яку не відкривав macOS
Сьогодні займався, напевно, найстрашнішою побутовою компʼютерною задачею — відновленням даних з жорсткого диска. 💣
Як це трапилось. В мене був зовнішній диск, відформатований в APFS - файлову систему Apple. Диск був зашифрований - APFS це вміє на рівні файлової системи, дуже зручно.
Все було добре. Аж допоки мені не знадобилося перенести macOS на новий ноут. З наявних способів перенесення найшвидшим було через резервну копію - Ethernet під рукою не було, а USB-C виявився дуже повільним. Але для цього як раз потрібний вільний диск або хоча б розділ. Тут стала до нагоди ще одна можливість APFS - вона також вміє створити два розділи, що будуть розташовані в єдиному просторі на диску. Тобто, я зміг створити додатковий незашифрований розділ на тій самій флешці без фізичного виділення під нього місця. (До речі, не знаю, може Linux теж так вміє.)
Перенесення системи пройшло без проблем. Проблеми виникли трохи пізніше. Після чергового оновлення macOS моя флешка припинила відкриватись. Причому найстрашнішим чином — навіть Disk Utility не міг нічого про неї сказати, а зависав. Виправлення диска відмовлялося казати хоч щось, окрім загальної помилки. Я вже майже вирішив, що флешка (а точніше, зовнішній SSD з терабайтом цінних даних) незворотно поламана. Це було дивно ще й тому, що останнім часом я нічого не писав на ту флешку — окрім згаданої резервної копії, звісно.
Промінь надії майнув від утиліти Disk Drill, яка змогла показати каталог диска (хоча файли відновити не змогла). Це, а також дивна поведінка Disk Utility, дало мені ідею, що справа не в диску, а все ж таки в багах macOS. Довго чекав, що macOS виправиться, але з минулого літа нічого не змінилось.
Тому далі я знайшов оцю статтю, а через неї — драйвер apfs-fuse. Але на моєму Маці все до купи не зібралось, через те, що флешка була постійно зайнята Disk Utility, та драйвер FUSE не міг отримати до неї низькорівневий доступ. (FUSE це цікава технологія, відкриває можливість відобразити у вигляді директорії практично будь-що, до чого є драйвер. Наприклад, S3.)
Тому наступне, що довелось робити, це встановити Ubuntu на мій ігровий компʼютер (бо на M2 Macbook Air навіть не хочу пробувати.) Про це треба окремо розказувати, але головне, що на Ubuntu цей apfs-fuse з першої спроби скомпілювався, примонтував мою флешку, та без жодних заперечень скопіював з неї дані.(Пароль від диска він питає під час монтування.) Якось так:
sudo apfs-fuse -o allow_other /dev/sdc2 ~/external
Такі справи. Де macOS зависає, аматорський драйвер для Linux працює.
17.03.2023
Що робити з invalid byte sequence in UTF-8
Сучасним програмістам не доводиться багато думати про кодування рядків. Так склалось, що UTF-8 переміг, та ми можемо вважати, що всі рядки та тексти будуть в UTF-8, та майже ніколи не матимемо проблем. Як приклад: всякий файл формату JSON за стандартом має бути в кодуванні UTF-8. І це чудово, бо на початку моєї карʼєри реально треба було обирати кодування — для кирилиці одне з двох чи трьох; для латиниці інше; та всі вміли перекодувати текст як треба.
UTF-8 класне кодування; для латиниці воно дуже компактне, але при цьому дозволяє використати більш мільйона різних символів, що легко покриває всі мови світу, а також емоджі, системні символи та все інше. Але недоліки UTF-8 - це змінна довжина символу та те, що не кожна послідовність байтів є дійсним рядком UTF-8. Перше заважає, коли треба порахувати “наочну” довжину рядка, або розбити його за символами. А про друге доводиться деколи згадувати. Класична дірява абстракція!
Якби будь-який рядок був дійсним UTF-8, то можна було б хоча б ігнорувати кодування та обробляти всі рядки в UTF-8. Для випадків, де не треба інтерпретувати зміст рядка, цього цілком достатньо — ну є незрозумілі символи та і є. Але ж ні — деякі рядки мають недійсні послідовності символів, та їх не можна інтерпретувати як рядки UTF-8 в принципі, принаймні, не без втрати цих недійсних послідовностей. Тому стандартні бібліотеки, коли натраплять на недійсний символ, не проковтнуть його, а викличуть помилку — в Ruby це класично nvalid byte sequence in UTF-8.
Ми стикнулись з цією проблемою при обробці DNS записів; вони не мають визначеного за стандартом кодування. Ясно, що в більшості випадків DNS містить рядки ASCII, який фактично є підмножиною UTF-8, тому ми їх обробляли як UTF-8 і все було добре. Проте знаходяться записи, що викликають помилку кодування. Тому доводиться всі рядки приводити до UTF-8 перед обробкою. В Ruby для цього є метод encode: string.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: '�'). Тільки так можна “вичистити” рядок від недійсних символів. Є ще метод force_encoding, але він нам не підходить — бо він не перетворює рядок, а тільки встановлює кодування. force_encoding корисний, коли відомо, що рядок не в UTF-8, а в іншому кодуванні.
16.03.2023
Мої трохи контроверсійні погляди на приготування кави вдома
Кава, кава, кава. Не знаю, як так вийшло, що за двісті з лишком постів я ще писав про каву, бо приготування кави — то моє найтриваліше хобі. Сьогодні відкрив для себе сервіс Спеш — це підписка на каву з підбором до смаку, яка починається з чотирьох примірників кави без маркування, тож можна занулити свої преференції та дійсно звернути увагу на смак, а не бренд чи назву. Як на мене, ідея чудова, якщо хочеш спробувати, то з моїм промокодом 7832 знижка 10%.
Але взагалі, хотів поділитися своїми трохи контроверсійними поглядами на приготування кави вдома.
Найпростіший спосіб отримати вдома дуже хорошу каву — холодна екстракція. Купуєш пачку кави, молотої, приносиш додому, заливаєш в банці холодною водою. Через добу залишається тільки процідити через фільтр і все. Такий концентрат можна розбавляти холодною водою, гарячою водою, мінералкою, тоніком, молоком… В будь-якому разі, такий домашній колд-брю матиме дуже мʼякий та насичений смак, і з ним майже неможливо наробити помилок. Якщо все ж таки потрібна “нормальна” кава, то для дома найкраще підходить Aeropress. Аеропрес робить хорошу каву та головне, ним легко користуватись, та знов таки, складно помилитись. Ще аеропрес не прискіпливий до помелу.
Два головних фактори якості кави — свіжість обсмажки та свіжість помелу. Який там сорт — справа третя. Обсмажка помітно старішає за місяць, помел — не слухайте снобів — за декілька днів. Якщо вдома ще немає кавомолки, то краще вже професійний помел, який вам зроблять в крамниці, ніж бюджетна кавомолка. Також тому ніколи не варто переплачувати за каву з супермаркету — вона гарантовано надто стара, щоб відповідати ціні. Якщо хочеться витратити гроші на каву, треба йти до обсмажувачів — тим паче в Україні це надзвичайно розвинена індустрія, з пропозицією на будь-яку кишеню. От зі свіжою місцевою обсмажкою вже можна обирати якийсь сорт.
Крутий еспресо вдома — дуже дорого. Причому більша частина бюджету має піти на кавомолку, як це не дивно звучить. Проста еспресомашина — не перешкода, в мене хорошу каву робить ручна Picopresso. Але без якісного рівномірного помелу ніяка еспресомашина, навіть скажено дорога, не зробить хорошої кави. Навіть навпаки - “казуальні” еспресомашини, в тому числі всі автомати, мають на виході клапан тиску, який компенсує недоліки помелу та здатні зробити хоч якийсь еспресо з будь-чого. В серйозній машині з поганого помелу вийде брудна водичка. Не питайте, як я про це дізнався.

