Software Architect в CMK
  • В Італії добре, але в Україні краще. Чому я повернулась додому через три місяці

    У нас майже немає русні. А так все просто.
    Пароль: Слава Україні.

  • В Італії добре, але в Україні краще. Чому я повернулась додому через три місяці

    Так, є таке. Тут не багато, але все одно стрьомно

  • В Італії добре, але в Україні краще. Чому я повернулась додому через три місяці

    У нас поруч є готель, де живуть наші дівчата з дітьми, що евакуювалися із Запоріжжя. Повний пансіон за рахунок, я так розумію, ООН.

    Ми запитаємо, куди звернутись, щоб потрапити на ті самі умови.

    Якщо, Ви ще в Італії, напишіть мені на пошту: [email protected], може вдасться якось допомогти.

  • В Італії добре, але в Україні краще. Чому я повернулась додому через три місяці

    Цікаво було порівняти досвід.

    Ми приїхали до Італії на пару тижнів пізніше за Юлію всією родиною: жінка, донька, я, наші котики та маленький песик.

    Одразу скажу, що виїхав я з України на законних підставах (стан здоров’я), нікому ніяких хабарів не давав, і ніякого супротиву або заперечень з боку військових та прикордонників не мав.

    У першій же день паребування в Італії я придбав картку Vodafone Italy по нашій Українській ID картці, ніякого кодічі фіскалє від мене не вимагали. Потім так само на свою ID картку оформив сімки для жінки та доньки (а кодічі фіскале оформлено на закордонний паспорт).

    На третій день, ми здали ПЛРи та отримали медичні документи — щось типу страховки. (Потім жінка з донькою з цими документами і без податкового звернулись до сімейного лікаря). Того ж дня, стали в «чергу» до квестури для отримання тимчасового захисту. За два тижні подали заяву та отримали документи щодо тичасового захисту з фіскальними кодами.

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

    Як на мене, Італійська бюрократія не більша, і не гірша за нашу.

    Щодо медицини ... нажаль, довелося зіткнутися. Донці стало дуже зле через початок хронічного захворювання.

    Звернулися до лікарні. Ніхто взагалі ні про які документи не питав. Одразу почали рятувати. Гвинтокрилом доправили др сусіднього міста (100 км, але через ремонти на автобанах лікарі вирішили перестрахуватись). В цій лікарні документи запитали — для отримання відшкодування від держави.

    Погодьтесь, що в Україні ніхто дітей гвинтокрилами не возить. Ми доньку на операцію з апендициту до Київського Ахматдету власною машиною з Бучі везли ...

    Але загалом з медициною так — дійсно заморочок багато. І без знання італійської буде дуже важко — Google Translator, до речі, дуже рятує.

    Аренду житла мені важко коментувати — бо 8 котів, це те що робить випадок досить особливим. Але нам пощастило: ми живемо в окремій хоч і невеликій квартирі дуже близько до моря.

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

    «Живуть минулим» ... це, як на мене, трохи неетично з боку Юлі так говорити. Те, що вона вважає за майбутнє, може таким не вважатися іншими людьми. Та і в Україні вистачає людей, що (принаймні до війни) просто насолоджувалися життям.

    Супермаркети в Італії — таке відчуття, що всі працють за бізнес-моделлю АТБ. Дійсно в кожній мережі дуже обмежений вибір. Але є вибір мереж :) Але без машини — труба.

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

    Чи ми різні? Як водій склажу, що їздять італійці один в один, як ми. Навчіть трохи гірше ) бо мені скидається, що 4 з 5 італійців мають травматичний досвід керування bmw — геть не чули про поворотники. Тож, якщо їздять, як ми ... то і в решті не дуже від нас відрізняються

    Мінуси, які б я відмітив:
    1. Брудно (навіть зв собаками не прибирають) — але це може стосуватися лише нашого «села»

    2. Шумно. Італійці (піівдегні) дуже гучні. Кожен наче маленький вулкан Везувій. Про північних кажуть, що там з цим набагато краще — але особисто з ними не спілкуваася

    3. Важко з англійською. Дуже багато, хто не говорить і не розуміє.

    4. Автобани — блін, вони стрьомні. Це вам не наша злітна смуга: Київ — Бориспіль. Це тунелі, підйоми, повороти та узвози, і досить часто все це одразу. Але вже потрохи звикаю (десь на 3й тис.)

    І це, мабуть, все.

    Мені Італія подобається. Гарна вона.

  • Прогресивний TypeScript. Поступово і з мінімальними зусиллями

    Ну так гарно почали, а закінчили на рівні «я сказав». Сумно якось.

    Я вибачаюсь, якщо когось образив та/чи засмутив — я не хотів.
    Єдине, що мене трохи виправдовує, — я все таки не просто сказав, а показав на прикладі — це все ж такі різні речі.

    Ви не відповіли на питання «навіщо». Що вам, як розробнику дає string ’api/user’ ? Там взагалі може проксі стояти або реврайт і запит іде кудись вліво. А якщо вам як архітектору (наприклад) зручно бачити все в одному місці — то, на мою думку, для цьго краще використовувати інструменти для цього призначені. Той самий свагер на приклад, а не зводити 20 ендпоінтів в один файл.

    Погоджуся, що «зручно» — то є суб’єктивно, і тому поясню, чому це зручно саме для мене.
    Власне і одразу згадаємо сваггер

    1. Якщо в АПІ немає доки — то її треба зробити перед тим, як цей АПІ використовувати (що найменьше — ті енд-поінти, що планується використовувати). Це дуже зекономіть час на дебаг та тести. Фронтенд-розробник має право не брати задачу в роботу, якщо немає спеки до АПІ, що планується використовувати при рішенні задачі.

    2. АПІ-клієнт роблять не замість сваггеру (або спеки в іншій нотації), а на її підставі.

    Наприклад, ми маємо таку спеку: petstore.swagger.io
    Та схема описує контракт, згідно з яким ми будемо викликати сервер.

    В нашому прикладі Контракт каже:
    GET /users/:userId
    Повертає:
    — 200 User
    — 404 «Not Found»
    — 401 «Not Authorized»
    — 500 «Server Error»

    Це означає, що і нам треба в одну сутність поєднати відповідні речі клієнта:
    — метод
    — шлях
    — відносно кожної можливої відповіді:
    — статус
    — валідацію тіла (чи будь чого ще) відповіді
    — реакцію на відповідь

    Перелічення API розірває опис виклика за контрактом. В одному місці ми url USERS зробимо з методом POST, а в іншому — з методом PUT. Або підставимо іншу функцію валідації, або іншу функцію трансформації. Мало того, що ми отримаємо два варіанти для одного виклику, так вони по-різному працюватимуть і, навіть, з TS можна отрмати довгу дебаг-сесію. Мій підхід саме фокусується на тому, щоб один раз поєднати всі речі, а головне абстрагуватися від того, як само ми отримуємо дані.

    Власне відповідь на

    Що вам, як розробнику дає string ’api/user’

    — путь — це частина контракту, яку краще не маскувати і не відділяти від решти частин контракту.

    3. В прикладі петстор, енд-поінти згруповано. Це нормальна практика, що відповідає SRP. SRP — це не тільки про розділення, а і про об’єднання. Речі, що мають одну й ту саму причину для зміни, мають бути згруповані. Тому, це точно не треба розносити апі-коли до однієї групи енд-поентів (наприклад, одного ресурсу) по різних файлах.

    А якщо розмір бандлу є надто важливим то ми завжди можемо наш клієнт розбити на маленьки функції, що мають дуже класно трі-шейкатись (звісно, в залежності від налаштувань бандлера)

    наприклад такого клієнта
    ```
    const UsersApi = ({ get }: IHttpConnection) => ({
    getById: (userId: string) =>
    get(`/users/${userId}`).then(
    expected(
    result(200, User),
    status(404),
    errorOnAnyStatus(`Failed to get User<${userId}>`)
    )
    ),
    getAll: () =>
    get(`/users`).then(
    expected(
    result(200, array(User)),
    errorOnAnyStatus(`Failed to get users`)
    )
    ),
    });
    ```

    можна розбити ось так:
    ```
    export const getById = ({ get }: IHttpConnection) => (userId: string) =>
    get(`/users/${userId}`).then(
    expected(
    result(200, User),
    status(404),
    errorOnAnyStatus(`Failed to get User<${userId}>`)
    )
    );

    export const getAll = ({ get }: IHttpConnection) => () =>
    get(`/users`).then(
    expected(
    result(200, array(User)),
    errorOnAnyStatus(`Failed to get users`)
    )
    );
    ```
    або можна ще так (якщо у на проекті є редакс):
    ```
    export const getById =
    (userId: string) =>
    (dispatch; Dispatch, getState: () => State, { get }: IHttpConnection) =>
    get(`/users/${userId}`).then(
    expected(
    result(200, User),
    status(404),
    errorOnAnyStatus(`Failed to get User<${userId}>`)
    )
    );
    ```

    Щодо «шаблоний метод». Це трохи не той випадок. По-перше, шаблоний метод має чітку специфікацію на базі класів і передбачає наслідування. А по-друге, навіть, якщо подивитись на функція fetchApi, як на шаблоний метод (або швидше, як на стратегію), то в неї у перший крок додано всі наступні кроки.

    Реалізація стратегії мала б виглядати якось так:

    ```
    function apiRequest(
    fetchData: (...args: A) => Promise,
    validateData: (result: unknown) => result is T,
    transformData: (result: T) => D
    ) {
    return async function (...args: A): Promise {
    const result = await fetchData(...args);
    if (validateData(result)) return transfromData(result);
    throw new TypeError(`Invalid Server Response ${result}`);
    }
    }
    ```

    fetchApi використовує найулюбленішу ідіому JavaScript — Callback
    Причому, використовує її необгрунтовано, бо по-перше, навіть в такому виконанні можна обійтись і без передачі цієї функції, а по-друге сам по собі Promise вже має той самий метод для доєднання наступних кроків в алгоритмі здійснення апі-запиту.

  • Прогресивний TypeScript. Поступово і з мінімальними зусиллями

    Ні, якщо у вас різна логіка транспорту, то один узагальнений код для отримання даних не дуже підходить, це логічно :)

    Ну я ж показав на прикладі. Транспорт, як раз спільний. А ось логіка обробки результата — різна.

    Ну, і головне — опис клієнта в коді має бути максимально наближеним до його опису swagger-ом чи blueprint- ом, чи, навіть GraphQL-ною схемою. Власне тому, і зручно, що відносні шляхи видно одразу. Рівно, як і можливі відповіді.

    Ну і ще момент — стаття все ж таки не про ідеальний api клієнт, а більше про тайпскрипт, а код то скоріше до прикладу ніж до керівництва до дії.

    Ну, якби при ревью кода я не бачив копі-пасти з таких статей, то може і не коментував би їх зайвий раз.

    Тож, я підсумую:
    1. LSP у прикладі порушено
    2. Enum заради Enum
    3. SRP або Concern Separation порушено.

  • Прогресивний TypeScript. Поступово і з мінімальними зусиллями

    В рантайме то она и не надо, ибо будет замедлять код бесполезными проверками, если только тип переменной явно не проверятся, т.е от нее зависит логика функции, как при перезагрузке функций в TS. В ECMA же это тоже вообще бесполезно, за неимением их декларативного синтаксиса.

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

  • Прогресивний TypeScript. Поступово і з мінімальними зусиллями

    Щодо другого моменту — то в проекті там будуть лежати URL без базового шляху. Тобто, наприклад /api/user або просто юзер. А basePath буде братися під час білда під енв.

    — Воно має сенс, коли інші юзери можуть в інших місцях, вказати «щось не те». А так, все буде в одному файлі, то ж краще зробити більш зручним — бачити шляхи дуже зручено.

    А щодо третього моменту, то дивіться. В даному рішенні fetchApi просто виконує метод для перетворення DTO на доменну модель. Якщо прибрати цей функціонал, вам доведеться це саме писати в кожному окремому fetchCar or fetchWhatEverGreen. Чи треба це?

    — саме так і треба, бо не все так просто, як здається спочатку.

    Я тут зробив маленький сендбокс і трохи тестів апі-клієнта:

    codesandbox.io/...​ewwindow=tests&theme=dark

  • Прогресивний TypeScript. Поступово і з мінімальними зусиллями

    Гарна стаття.
    Але є декілька зауважень

    1. Не робить хибних декларацій

    ts
    interface IAppRequestInit extends RequestInit {
      // перевизначаємо body**
      body: any;
    }

    Порушує LSP. `extends` означає, що похідний тип ми можемо використати всюди, де використовували базовий. Але не в цьому випадку. Ясно, що нам у цьому конкретному випадку такого не треба, але ж ми про TS.

    Тут, краще було зробити ось так:

    ts
    type IAppRequestInit = Omit<RequestInit, "body"> & { body: any }

    Результат той самий, але без декларацій про наслідування.

    2. Enum для url є просто зайвим.
    По-перше, на дуже багатьох проектах, адреса бекенда залежить від конкретного оточення (local, dev, staging, prod etc.) і задається конфігурацією. Дуже часто конфігурація визначається у момент збірки, але її визначення у рантаймі — теж є норм (один бандл для різних оточень — це круто).

    То ж, зазвичай урл буде вказуватися якось так: `config.SERBER_URL`

    3. Розрізняйте шари.
    Ми маємо дуже потужну функцію fetchApi, але вона робить забагато. Краще зробити два рівні:
    1-й — рівень підключення. Відповідає за встановлення загальних для всіх викликів параметрів та технічні функції, як-то серіалізація/десеріалізація у json
    2-й — рівень клієнта. Описує вже конкретні методи віддаленого сервісу в термінах того сервісу. Власне, тут і визначаються типи для Payload та Result.
    В попередніх коментах вже були згадки про fetchUser, fetchCar, etc. Так от власне в них і треба робити мапінг результату, отриманого з fetchApi.

    Я б хотів на своєму проекті побачити ось такого апі-клієнта.

    ts
    const ApiClient = ({ get, post }: HttpConnection) => ({
      getUser: (userId: string) =>
         get(`/users/${userId}`).then(castToUser),
    
      createUser: (userData: CreateUserData) =>
         post('/users',  userData).then(castToUser),
    });
  • Выбираем идеальную клавиатуру для программирования. Личный опыт поиска

    Я тоже остановился на 75% от Keychron. Но мне не светит поменять ABS на PBT, по крайней мере в ближайшем будущем.

    Я взял Keychron K3 с Getheron Low Profile Brown свитчами.

  • З ІТ-ринку — у фінансовий. Український програміст — про життя й роботу в Канаді та причини повернутися додому

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

  • «Я не хочу так работать». Как медик освоила IT и почему ушла через 3 года

    Да, да, да — все правильно. Ум хорошо устроенный лучше, чем ум хорошо наполненный! А еще лучше ум, правильно мотивированный. Я только за! ;) И да, 100% делать надо то, что нравится.

    Просто, возьми Зайца, а не Ежика ;) Заяц уже опытний — его курить учили ;)

  • «Я не хочу так работать». Как медик освоила IT и почему ушла через 3 года

    Все правильно )
    Но, с ежём ты погорячился )

    Ёжик самый в лесу невпихуемый зверь ).

    Лучшее (нет, правда) в комментарии:

    IT — в первую очередь про кайф от решения задач, а во вторую очередь — про знания
  • «Я не хочу так работать». Как медик освоила IT и почему ушла через 3 года

    Собственно, проблема Ани, героини статьи, заключалась в том, что она на самом деле ИТ НЕ освоила, что стало причиной невозможности сменить работу.

    И именно потому, что освоить ИТ не получилось, то решение уйти из ИТ — абсолютно правильное.

    Потому, что если первый раз не вышло, то — парашютный спорт не для вас )

  • «Строгий» JavaScript: зачем и кому это надо

    Сейчас как раз и решаем — использовать типизацию или нет.
    Склоняемся к тому, что надо использовать.

    Плюсы типизации:
    1. уменьшаем риски багов (до 15%)
    2. облегчаем переиспользование за счет подсказок со стороны IDE

    Минусы:
    1. Больше кода
    2. для утилит (где много дженериков) гораздо больше кода
    3. необходимость добавления .js.flow, .d.ts для старого кода

  • «Строгий» JavaScript: зачем и кому это надо

    Очень хорошая статья в отношении того, типизировать или не типизировать:
    blog.acolyer.org/...​table-bugs-in-javascript

  • «Строгий» JavaScript: зачем и кому это надо