Обновить
4

Пользователь

1
Подписчики
Отправить сообщение

А вот представьте, если реально ИИ заменит программиста полностью. Это же какое счастье будет, ведь если можно заменить настолько сложную профессию, значит ИИ достаточно умен чтобы ввести бизнес. Он достаточно умен чтобы программировать роботов, а потом этими роботами управлять.
Тогда достаточно недели, и через неделю будет автоматизирован весь мир.

Да, можно выделить общее. Но FSD и прочие предлагают выделять не общее, а сразу диктуют структуру.

Можно выделять общие рецепты, и мы о них уже знаем (Solid, Grasp)

Можно выделять паттерны для компонентов, и мы о них тоже знаем (render props)

А знаете почему все эти проблемы с архитектурой возникают? Может просто потому, что каждый проект уникален, и для каждого проекта нужна своя уникальная структура, со своими уникальными ограничениями?
И тогда вместо FSD, люди бы учили, а как правильно разделить код, зачем вообще его разделять, почему бы не писать все в одном файле и тд

Высокоуровневые языки давали абстракцию, скорость но что-то забирали. Например контроль над памятью. И программы стали тяжелее, но мы смирились.

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

Нужна ли нам такая абстракция?

Это открыли еще в прошлом веке. Сейчас есть решения поинтереснее, например tRPC, и скорость разработки взлетает x2-x3

Ага, а можно было бы еще вместо картинок отображать видео с одним кадром, и загнать под DRM. И не придумывать такую сложную архитектуру

Ну окей, вложенность сильная, рекурсивные запросы. А что, индексы не помогли? Почему запросы такими долгими были? Или там было прям очень много запросов одновременно? Имхо база справится спокойно, если корректно индексы настроить.

Читал статью и были точно такие же мысли. В статье классический алгоритм несчастья:

Обесценить то, что имеем, и переоценить то, что не имеем.

Здесь в UserInfo передаётся весь объект user, хотя используются только два поля из всего объекта: name и age. Это делает компонент зависимым от структуры объекта и усложняет изменения.

А вот и неправда) все зависит от того, какой уровень абстракции у вашего компонента. Если это UI компонент, то да, ваш пример имеет место быть. А если это компонент с бизнес логикой, то правильнее передать весь user, а не отдельные поля. Потому что может произойти так, что в компоненте нужным станет еще и поле 'gender'. И если вы все передавали через пропсы, а компонент использовали в 100-200 местах, то получается что все 100-200 мест нужно изменить, а если передавали бы user, то при потребности в новом поле изменения бы коснулись только одного места.

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

Если бы было все так просто как вы показали, то мы бы давно написали линтер, который работает по вашим советам, и делает код лучше

Автор говорит о проблемах тестирования, что оно делается сложным из-за того что нужно много зависимостей внедрить.

Хотя в своем примере тестирования тоже мокает зависимости, просто в новом подходе это делается чуть проще.

Ну а что мешает один раз написать моки для этих зависимостей и переиспользовать?

Новая архитектура нарушает SOLID, хотя автор этого не видит. Автор не понимает, что принцип единственной ответственности нужно рассматривать на определённом уровне абстракции. Например:

/router.tsx отвечает и за создание роутера, и за рендеринг

Нет, router.tsx отвечает за роутинг (единственная ответственность). А роутинг в себя включает и определение роутов, и их отображение отталкиваясь от window.location, и их композицию и тд.

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

А роутинг в одном файле - очень удобно, нежели искать по проекту кто как и где рендерится

А тот факт, что бот Яндекса будет иметь доступ к каналу, и сможет выкладывать что угодно, и когда угодно?

Я не понял, почему когда абстрагируемся, связи остаются такими же, что и на низких уровнях (HTTP запрос между микросервисами).

Пример: На уровне класса, связи - прямые вызовы. Дальше по абстракции может быть например несколько процессов, которые вызывают друг друга через IPC. Дальше несколько сервисов через общую шину. Дальше несколько систем через gRPC.

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

Окей проблема понятно. А вот почему через typescript решили, непонятно.
Посмотрите в сторону zod (валидатор). Схема описывается через JS, но можно вывести typescript типы.
+ вам для low code платформы нужна будет валидация данных, например чтобы число было положительным, или чтобы строка была не длиннее чем X символов.
А как вы это сделаете через typescript?
Решение я описал в первом комментарии, и как будто больше подходит. А еще вопрос поддержки. Вот вы разобрались, понимаете как парсить ast дерево, но задача у вас простая, и придется поддерживать этот кому-то этот костыль....

А мне так и непонятно, какую проблему вы решили? Вы хотели использовать и typescript и proptypes, но описывать одну схему?
Если да, то не легче было просто общую схему описать, которая бы генерила proptypes, и выводила typescript схему?
То есть использовать code first, а не schema first

Да, простите, пропустил. Но мой пример лучше, так как пример в статье сломается, если дочерний компонент вызывет этот коллбэк в useLayoutEffect сразу при маунте.

А я бы еще добавил важный хук:

export const useCallbackRefed = <T extends (...args: any[]) => any>(callback: T): T => {
  const ref = useRef(callback);
  ref.current = callback;

  return useCallback<T>(
    ((...args: any) => {
      return ref.current(...args);
    }) as any,
    [],
  ) as T;
};


Зачем он нужен? Это тоже самое что и useCallback, но без зависимостей. И его можно безопасно использовать в useEffect, а еще безопасно передавать как props

А автор не подумал, что если теперь есть soft delete, то старые запросы нужно тоже переписать? Ведь запрос на сущность, которая теперь soft удалена, должен не возвращать данные, а он продолжает возвращать. И если запросы бы были переписаны, то partial index бы сработал. Тут факап сразу в нескольких местах. А еще можно было синкануться с коллегами, чтобы хоть кто-то другой знал план, и бы заметил что что-то не так в этом плане...

Да, идея классная, абстрагироваться от всего, и сначала писать абстракцию, а потом реализацию. Только вот примеров мало, а потому что на практике этого очень сложно достичь. Да и на самом деле повышается сложность всего проекта, порог входа. Ну и не забываем, что за абстракцию придется заплатить. Как связать изменения данных в абстракции, и уведомление реализации о том, что ей нужно обновиться? В статье нет примера, но реакту, чтобы обновиться, нужно какое-то уведомление. Тогда у абстракции должен быть механизм подписки на свои уведомления. И вот мы получили MobX, который можно потом привязать позже к любому фреймворку, если грамотно все сначала настроить/разделить.

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

А еще, как часто у вас возникал сценарий, когда вы хотели пересесть на другой фреймворк? Даже если у вас все абстрактно, пересесть на другой фреймворк будет очень дорого, ведь придется потратить много ресурсов на тестирование, и переписывание части реализации. И где-то, да и придется снова костылить утечки абстракции.
Тогда не легче, просто разделить приложение, сделать из него микрофронты, и писать каждую часть на удобном себе фреймворке? Да, получим сложную доставку, сложную инициализацию, но зато каждую часть можно будет реализовать именно так, как нам удобно.

Да и в целом, в мире бэкенда давно двинулись от идей чистой/идеальной архитектуры, в сторону микросервисов. Не замечаете некую схожесть в решении этих проблем?

В React нет такой возможности

А useRef? А держать состояние вне компонента?

Я посмотрел на Fusor. В React тоже можно так сделать с помощью ref-ов. Все данные можно хранить в ref-ах, а когда нужен перерендер, использовать для этого useReducer:

const [_, forceUpdate] = useReducer((c) => c + 1, 0);
const myData1 = useRef();
const myData2 = useRef();

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

Fusor - не разделение ответственности, потому что философия React в том, чтобы реактивно показать данные, сматченные на верстку. И тут как раз и получается, одна ответственность - реактивно отображать данные, с чем React отлично справляется.

1

Информация

В рейтинге
6 841-й
Зарегистрирован
Активность

Специализация

Фронтенд разработчик
Старший