Pull to refresh

Comments 18

UFO just landed and posted this here

Не соглашусь, что отсутствие TypeScript делает проект хуже без вариантов. В статье осознанно не затронуто, но для данного подхода намного элегантнее получается как раз без TypeScript.

Вот такой типовой пример с TS, когда инлайним тип:

function welcomeClient({
  name,
  phoneNumber = ``,
  email,
  company,
  trialVersion = true,
}: {
  name: string;
  phoneNumber?: string;
  email: string;
  company: string;
  trialVersion?: boolean;
})

Можно оценить тонкий привкус синтаксиса с опциональными пропертями, которые нужны, чтобы подружить значения по умолчанию с TS. Это ещё не докручено значение по умолчанию самого объекта :)

По последней ремарке насчет 2-3 параметров. После интенсивного обсуждения мы, как команда, решили применять даже для 1 аргумента. Это делает принятие решения не opinionated. Ревью тоже очевидно. Главным аргументом было то, что нужен опыт, чтобы принимать решение о том, что в этом конкретном случае пора менять на объект и рефакторить. Этому сложно научить начинающих специалистов. Опытные все решают по-разному. Наше решение полная единообразность в этом случае.

UFO just landed and posted this here

TS сам по себе не панацея. Из нашего опыта большие проблемы проекта лежат не в плоскости наличия или отсутствия проверки типов. Скорее в области архитектуры проекта, тестируемости решения, loose coupling и документирования переиспользуемых паттернов.

У тех команд, кто решили попробовать этот подход получит распространение. Также как и TS у тех, кто решил на него переехать и тд и тп.

Мы бы советовали прочитать книгу Пять пороков команды. То что вы упомянули это один из пороков: Необязательность. Из него вытекает саботаж. Это необходимо однозначно пресекать.

Если у вас здоровая функционирующая эффективная команда, то и новый член этой команды либо адаптируется под общие цели команды, либо уйдет из неё, несумев предпочесть цели команды своим личным. Ну или команда развалится, такое тоже возможно.

UFO just landed and posted this here

Работаем с зарубежными «большими пацанами». 3 года назад TS был в опале, сейчас ситуация поменялась, он используется по умолчанию. Есть большая проблема того, что те, кто не писал бекенд и начал сразу с фронтенда и работает давненько просто используют TS ужасно и становится хуже чем без него. Хотя опять же это скорее не проблема TS как такового, а проблема Engineering Excellence для этой конкретной команды.

Мы бы сказали, что если команда не может выстроить нормальную архитектуру решения без TS, то с ним станет все только хуже. Такая же история с применением DDD, когда в команде и без него никто не может сделать простое понятное решение.

Штука с generics в том, что они абсолютная пушка, например, в C#. Но в JS они были всегда, вся эта утиная типизация, все итак уже generic. Это одна из самых сложных тем, которой надо учить коллег фронтендеров, знакомящихся с TS. Дается тяжело, хотя они всегда так и работали, просто не явно и это им ничего не стоило.

Презентовали эту идею опытным иностранным коллегам, все задумались, было очень интересно это отметить. Всем показалось, что есть смысл, но ведь так никто не делает. Ещё было интересное опасение, что при таком подходе, когда очень просто расширять код без изменения клиентов, куда проще писать говнокод.

Мы решили делать так для начала только в своей команде. У нас там нет TS, поэтому выглядит очень изящно и джунам проще сориентироваться, пока книга «Чистый код» ещё не осилена. Пробуем в другом проекте, где есть TS, выглядит куда более громоздко.

Посмотрим labeled tuples, спасибо :)

Ок у вас проект на 500К строк кода, и вдруг соседний отдел поменял значительно типы каких то рестов, в рамках какой то очень важной работы, дтошки от которых повсюду используются, и вот теперь надо это вкрутить в свой код

Как без TS можно быть уверенным что точно не накосячил?

TS не защитит вас в рантайме. Если придет что-то другое, то вы это TS не излечите и он вам об этом ничего не скажет.

Интеграционные тесты API должны гарантировать, что эта штука все ещё работает. Тесты API на контракт тоже должны быть здесь. e2e тесты клиента должны проверять полную интеграцию для минимального user happy path.

Недавно мы начали разрабатывать проукты через TDD, в довольно строгом смысле. Это требует наличия инфраструктуры, которая поддержвает все уровни тестов, включая линтинг от TS.

Как мы решаем тот случай, о котором вы говорите:

  1. API предоставляет контракт в виде npm пакета с автосгенерированным TS кодом (в нашем случае поверх gRPC и protobuff). Следуем SemVer: патч, фича или мажорный апдейт контракта сервиса.

  2. BFF на NestJS использует пакет с типами от API везде в коде. Не создаем дополнительные слои DTO.

  3. В контроллерах в обязательном порядке маппим данные перед отправкой на клиент, чтобы в рантайме не ушло больше чем мы хотим.

  4. Если меняется, используемый API, то обновляем его npm-пакет и смотрим отвалилось ли что по TS линтингу и тестам всех остальных уровней: юнит тесты BFF, интеграционные тесты BFF, e2e тесты клиента.

  5. При этом во фронте у нас проект без TS.

В целом мне в BFF очень нравится такой флоу работы. В фронтенде при работе со своим BFF мы стараемся идти в сторону только обратно совместимых изменений API, чтобы не афектить старый UI и новый UI. И только после того, как старого не остается ни у кого, тогда клинапим BFF обратнонесовместимым обновлением. Следуем подходу двухфазного деплоя.

Можно в этом же файле создать интерфейс и назвать IWelcomeClientArgs и он ещё в другом месте пригодится, там где будите этот объект с параметрами формировать

Да, так и есть :) Иногда не создаем типы, чтобы использовать их 1 раз. Хотелось честно показать, как не очень это может выглядеть вместе с TS.

Поздно, если нет TypeScript это уже неладно.

Это почему ?
По наблюдениям. Если человек не умеет писать на js, ему и TypeScript не поможет.
И большая часть проектов, которые мне пригодились были написанны без type script.

А использовать будем уже так:

const {
  loadedClient: loadedSender,
  isLoading: isLoadingSender,
} = useClient(senderId)

const {
  loadedClient: loadedReceiver,
  isLoading: isLoadingReceiver,
} = useClient(receiverId)

Мои поздравления.

Мне только не нравится, если все аргументы опциональные, то нужно передавать пустой объект

Кажется, что это решается значением по умолчанию так:

function welcomeClient({
  name,
  phoneNumber = '',
  email,
  company,
  trialVersion = true,
} = {})

Так большинство библиотечных хуков и сейчас возвращает объект, а не массив. Вроде как.

С параметрами функций - надо все-таки потоньше. Хорошая идея сразу принимать объект options, если начинаются всякие необязательные параметры. Но можно обязательные 1-2 и отдельными оставить.

А вот у нас коллегам было не так очевидно, что объект лучше, чем массив. Было мнение что массив, как у useState, это react way.

Мы долго обсуждали оставить ли исключение для случая с 1 параметром, особенно, если это какая-то id. Также и про 2 параметра. Сошлись на том, что всем проще всегда оборачивать в объект, чем принимать такие решения. С ростом числа аргументов вовремя решаться на рефакторинг не всегда удается даже опытным разрабочикам. А ещё не ясно как проводить ревью.

Решили проблему не решать :)

Когда это значение и следом его сеттер, то это react way, а к вашему случаю это не относится

Не соглашусь с вами. Не буду размышлять на тему что такое react way и есть ли он вообще. Или на тему того, что хуки это может быть очень спорное решение и некоторые простые вещи приходится писать через одно место.

Нельзя ориентироваться на useState и считать это истинным react way. Это просто одна из многих функций и вот она так реализована и это имеет смысл. Это не значит, что все надо делать так на автомате.

Sign up to leave a comment.

Articles