Breaking changes

Breaking changes — это изменения в библиотеке или API, которые нарушают обратную совместимость. После внедрения таких изменений существующий код, написанный для предыдущих версий, перестаёт работать без модификации. В экосистеме React и современных библиотек пользовательского интерфейса breaking changes чаще всего связаны с:

  • изменением API компонентов;
  • удалением устаревших свойств;
  • изменением поведения компонентов;
  • изменением структуры пакетов;
  • изменением типов (в TypeScript);
  • изменением структуры DOM, создаваемой компонентами.

В контексте библиотеки Radix UI breaking changes могут влиять на архитектуру компонентов, систему композиции, правила управления состоянием, а также на внутренние примитивы, на которых строится интерфейс.

Понимание природы breaking changes особенно важно при обновлении библиотек между мажорными версиями, поскольку именно они содержат изменения, нарушающие обратную совместимость.


Семантическое версионирование

Radix UI использует принцип Semantic Versioning (SemVer). Версия пакета имеет формат:

MAJOR.MINOR.PATCH

MAJOR

Увеличение MAJOR версии означает наличие breaking changes.

Пример:

@radix-ui/react-dialog 1.x → 2.x

В этом случае возможны:

  • изменение API компонентов
  • удаление устаревших возможностей
  • изменение структуры импортов
  • изменение логики управления состоянием

MINOR

Изменения, добавляющие функциональность, но не нарушающие совместимость.

Пример:

  • добавление новых props
  • новые компоненты
  • дополнительные варианты поведения

PATCH

Исправления ошибок без изменения API.


Типы breaking changes в Radix UI

Изменение API компонентов

Одной из самых распространённых форм breaking changes является изменение props.

Пример старого API

<Dialog open={isOpen} onOpenCha nge={setOpen}>
  <Dialog.Content>
    ...
  </Dialog.Content>
</Dialog>

После обновления API может измениться структура компонента:

<Dialog.Root open={isOpen} onOpenCha nge={setOpen}>
  <Dialog.Content>
    ...
  </Dialog.Content>
</Dialog.Root>

В таком случае требуется изменить структуру компонентов.


Удаление устаревших props

В процессе развития библиотеки некоторые свойства помечаются как deprecated, а затем удаляются.

Пример устаревшего свойства:

<Popover align="center">

После breaking change свойство может быть заменено:

<Popover align="middle">

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


Изменение структуры компонентов

Radix UI активно использует композицию компонентов. Иногда структура может меняться.

До изменения

<Tabs>
  <Tabs.List>
    <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="tab1">
    Content
  </Tabs.Content>
</Tabs>

После изменения

<Tabs.Root>
  <Tabs.List>
    <Tabs.Trigger value="tab1">Tab 1</Tabs.Trigger>
  </Tabs.List>
  <Tabs.Content value="tab1">
    Content
  </Tabs.Content>
</Tabs.Root>

Изменение требует обновления всех мест использования компонента.


Изменение структуры пакетов

Breaking changes могут затрагивать импорты.

Старый способ

import { Dialog } from "@radix-ui/react-dialog";

Новый способ

import * as Dialog from "@radix-ui/react-dialog";

Такой подход используется для поддержки namespace imports, который облегчает масштабирование API.


Изменение поведения по умолчанию

Иногда библиотека меняет дефолтное поведение компонентов.

Пример:

До обновления

Popover автоматически закрывался при клике вне компонента.

После обновления

Поведение может измениться и потребовать явного указания:

<Popover.Root modal={false}>

Без явного указания логика работы может отличаться от прежней.


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

Radix UI активно использует порталы (React Portals) для модальных окон, тултипов и других overlay-компонентов.

Breaking change может изменить:

  • контейнер портала
  • стратегию позиционирования
  • порядок элементов в DOM

Пример:

Старый вариант

<Dialog.Portal>
  <Dialog.Content />
</Dialog.Portal>

Новый вариант

<Dialog.Portal container={document.body}>
  <Dialog.Content />
</Dialog.Portal>

Теперь контейнер портала может требовать явного указания.


Изменения в системе позиционирования

Radix UI использует библиотеку Floating UI для позиционирования всплывающих элементов.

Breaking changes могут включать:

  • изменение названий props
  • изменение алгоритма позиционирования
  • изменение поведения collision detection

Пример:

До изменения

<DropdownMenu.Content sideOffset={5}>

После изменения

<DropdownMenu.Content alignOffset={5}>

Изменения в доступности (Accessibility)

Radix UI уделяет большое внимание accessibility (a11y). Иногда breaking changes внедряются для соответствия стандартам:

  • ARIA
  • WAI-ARIA
  • WCAG

Изменения могут включать:

  • обязательные aria-атрибуты
  • изменение ролей элементов
  • изменение структуры DOM

Например:

<Dialog.Title>

может стать обязательным элементом внутри Dialog.Content.


Изменения типов TypeScript

Radix UI активно использует TypeScript, поэтому breaking changes часто затрагивают типизацию.

Пример:

Старый тип

type Align = "start" | "center" | "end"

Новый тип

type Align = "start" | "center" | "end" | "stretch"

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


Миграция между версиями

При переходе на новую major-версию обычно выполняется последовательность действий.

1. Проверка changelog

Каждый пакет Radix UI публикует changelog, где перечислены breaking changes.

Пример записи:

Breaking:
- renamed Dialog to Dialog.Root
- removed deprecated prop `forceMount`

2. Поиск устаревшего API

В кодовой базе ищутся:

  • устаревшие props
  • старые импорты
  • устаревшие компоненты

Иногда это можно автоматизировать с помощью codemods.


3. Обновление импортов

Если изменилась структура импорта:

import { Dialog } from "@radix-ui/react-dialog"

заменяется на:

import * as Dialog from "@radix-ui/react-dialog"

4. Обновление структуры компонентов

После изменения API требуется обновить иерархию компонентов.

Например:

Dialog → Dialog.Root

5. Проверка поведения компонентов

Даже если код компилируется, необходимо проверить:

  • открытие модальных окон
  • позиционирование тултипов
  • работу dropdown
  • взаимодействие с клавиатурой

Breaking changes иногда затрагивают именно поведение.


Автоматизация миграции

В крупных проектах обновление библиотеки может требовать изменения сотен компонентов. Для ускорения процесса используются:

Codemods

Codemods — это скрипты автоматической трансформации кода.

Обычно реализуются с использованием:

  • jscodeshift
  • Babel AST
  • TypeScript AST

Пример преобразования:

Dialog → Dialog.Root

можно выполнить автоматически.


ESLint правила

Некоторые команды добавляют eslint rules, которые обнаруживают устаревший API.

Пример предупреждения:

Deprecated Radix API detected: Dialog should be replaced with Dialog.Root

TypeScript ошибки

TypeScript помогает выявлять breaking changes на этапе компиляции.

Если типы изменились:

Property 'alignOffset' does not exist

это указывает на необходимость обновления API.


Стратегии безопасного обновления

Инкрементальное обновление

Лучше обновлять версии последовательно:

1.0 → 1.5 → 2.0

а не напрямую:

1.0 → 2.0

Отдельная ветка миграции

Создаётся ветка:

radix-upgrade

В ней:

  • обновляются зависимости
  • исправляются ошибки
  • тестируется интерфейс

Покрытие тестами

Breaking changes легче обнаружить при наличии:

  • unit-тестов
  • integration-тестов
  • visual regression tests

Особенно полезны визуальные тесты, так как Radix UI отвечает за интерфейс.


Наиболее типичные breaking changes Radix UI

На практике чаще всего встречаются следующие изменения:

  1. Переименование компонентов в *.Root
  2. Изменение namespace импортов
  3. Удаление deprecated props
  4. Изменение структуры порталов
  5. Изменение поведения overlay компонентов
  6. Изменение типов TypeScript
  7. Изменения в системе позиционирования Floating UI

Особенности архитектуры Radix UI, влияющие на breaking changes

Radix UI построен вокруг нескольких архитектурных принципов.

Headless-компоненты

Компоненты не содержат стилей и предоставляют только поведение.

Поэтому breaking changes чаще затрагивают:

  • props
  • composition API
  • события

Композиционная архитектура

Radix UI использует множество вложенных компонентов:

Dialog.Root
Dialog.Trigger
Dialog.Portal
Dialog.Content
Dialog.Title
Dialog.Description

Изменение любого элемента этой структуры может стать breaking change.


Контролируемое и неконтролируемое состояние

Многие компоненты поддерживают два режима:

controlled
uncontrolled

Например:

open
defaultOpen
onOpenChange

Изменения в этой логике могут потребовать переписывания части кода.


Предотвращение проблем при обновлении

Эффективная стратегия работы с breaking changes включает:

  • отслеживание changelog
  • использование фиксированных версий в package.json
  • регулярное обновление зависимостей
  • наличие автоматических тестов
  • использование TypeScript

Такой подход минимизирует риск критических ошибок при обновлении Radix UI и позволяет безопасно адаптировать кодовую базу к изменениям библиотеки.