Монорепозитории - плюсы и минусы

2 года назад·3 мин. на чтение

Есть множество вариантов решения проблемы масштабируемости проектов, один из них это монорепозитории. Однако как и у остальных подходов, у монорепозиториев есть как свои плюсы так и минусы.

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

Плюсы

Прозрачность и взаимодействие между командами

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

Простота в управлении зависимостями

Монорепозитории могут упростить управление внутренними и сторонними зависимостями. Все зависимости существуют в одной и той же кодовой базе, что помогает решать такие проблемы, как проблема ромбовидных зависимостей (diamond dependency problem). Например, вы можете закрепить конкретную версию зависимости, упростив проверку на критические изменения и обратную совместимость во всей кодовой базе, когда требуется обновление. Это означает, что управление уязвимостями в зависимостях также упрощается — вам нужно только исправить или обновить одну зависимость, что упростит одновременный запуск всех модульных тестов.

Масштабный рефакторинг кода

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

Минусы

Сложность ограничения контроля доступа

Контроль доступа также может стать проблемой. В некоторых случаях компания может не захотеть, чтобы каждый инженер имел доступ ко всей кодовой базе. Это также требует планирования, координации и настройки. Кроме того, очень сложно открыть исходный код только для части монорепозитория. Вытаскивать библиотеку, которую вы хотели бы выпустить с открытым исходным кодом, очень болезненно. С несколькими репозиториями открыть исходный код менее болезненно, потому что каждый проект уже находится в отдельном репозитории.

Протекающая абстракция

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

Ограниченная изолированность

При обновлении пакета нужно задеплоить зависящий от него проект (основное приложение), чтобы изменения вступили в силу. Монорепо не изолирует пакеты друг от друга полностью, как, например, это делает микрофронтенд архитектура. Ошибки деплоя подпроектов в монорепозитории также могут затормозить релиз основного проекта.

Быстрорастущий размер репозитория

Помимо того что репозиторий будет занимать много места, сборка проекта будет занимать много времени.

Размытие ответственности между командами

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

Резюме

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

Хуки useTransition и useDeferredValue в ReactJS 18

2 года назад·3 мин. на чтение

В React 18, релиз которого произошел в марте 2022, появилось много новых инструментов для написания производительных и отзывчивых приложений. Одним из заметных изменений является механизм рендеринга с новой ключевой концепцией: конкурентный рендеринг (concurrent rendering).

В этой статье повнимательнее рассмотрим два новых хука: useTransition() и useDeferredValue(). Эти два хука дают возможность определять приоритет обновления состояния, или, скорее, указывать, является ли обновление менее важным, чем другие, и откладывать его в пользу более срочных.
Какое обновление можно считать срочным, а какое обычным?
  • Срочные обновления: отражают прямое взаимодействие, такое как набор текста, клики, нажатия и т. д., т.е. то с чем взаимодействует пользователь. Когда вы вводите текст в поле ввода, вы хотите сразу увидеть введенный вами текст. В противном случае UI будет казаться медленным и подлагивать. Поэтому мы хотим сделать такие обновления приоритетным.
  • Обычные обновления: переход пользовательского интерфейса из одного вида в другой. Пользователи знают, что представление должно измениться или обновиться (например, когда ожидается ответ на запрос данных). Даже если есть небольшая задержка, это можно рассматривать как ожидаемое поведение, и это не будет восприниматься как медлительность приложения.
Итак, теперь подробнее рассмотрим эти два новых хука, объясним, когда их можно использовать, и посмотрим на конкретные примеры того, как их реализовать.

Хук useTransition() и функция startTransition()

До React 18 все обновления состояния помечались как "срочные". Это означает, что все обновления состояния обрабатывались одинаково с одинаковым приоритетом. С помощью useTransition() теперь можно пометить некоторые обновления состояния как несрочные.

Когда использовать useTransition() ?

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

Как использовать useTransition() ?

function App() {
 const [isPending, startTransition] = useTransition();
 const [searchQuery, setSearchQuery] = useState('');
 
 // запрос данных, который занимает некоторое время
 const filteredResults = getProducts(searchQuery);
 
 function handleQueryChange(event) {
   startTransition(() => {
     // оборачивая setSearchQuery() в startTransition(),
     // мы помечаем эти обновления как менее важные
     setSearchQuery(event.target.value);
   });
 }
 
 return (
   <div>
     <input type="text" onChange={handleQueryChange} />
 
     {isPending && <span>Loading...</span>}
     <ProductsList results={filteredResults} />
   </div>
 );
}

Хук useDeferredValue()

useDeferredValue() очень похож на useTransition() в том, что он позволяет отложить несрочное обновление состояния, но применяется его к части дерева. Это похоже методы debounce и throttle, которые мы часто используем для отложенных обновлений. React будет работать с такими обновлениями, как только завершатся срочные обновления.

Когда использовать useDeferredValue()?

С помощью useTransition() вы сами решаете, когда конкретное обновление состояния может быть помечено как менее срочное. Но иногда такой возможности может и не быть, например, если фрагмент кода находится в сторонней библиотеке. В таких случаях можно воспользоваться хуком useDeferredValue(). С помощью useDeferredValue() вы можете обернуть значение и пометить его изменения как менее важные и, следовательно, отложить повторный рендеринг. useDeferredValue() будет возвращать предыдущее значение до тех пор, пока есть более срочные обновления для завершения и отображения дерева с обновленным значением.

Как использовать useDeferredValue() ?

function ProductsList({ results }) {
 // deferredResults получат обновленные данные
 // когда завершатся срочные обновления
 const deferredResults = useDeferredValue(results);
 
 return (
   <ul>
     {deferredResults.map((product) => (
       <li key={product.id}>{product.title}</li>
     ))}
   </ul>
 );
}

Итоги

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