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

Проблема: ловушка фреймворков
Сегодня большинство UI-разработчиков завязаны на конкретных фреймворках вроде React, Angular или Vue. Они помогают быстро запустить проект, но привносят свои особенности:
Они требуют обновлений — что сегодня в тренде, завтра уже уже устарело.
Их абстракции протекают — логика часто знает о специфических особенностях фреймворка.
Они приводят к высокой связанности — логика привязывается к редаксу или к хукам React.
Они ограничивают мышление — мы начинаем путать архитектуру приложения с особенностями выбранного инструмента.
Предугадать, какие изменения претерпят фреймворки и браузеры в будущем, хотя и возможно теоретически, практически нецелесообразно. Мы оказываемся в роли догоняющих, а не опережающих, и чем больше проект, тем сложнее догнать текущие тенденции.
В таких условиях выбрать инструменты с расчётом на годы вперёд практически невозможно. Несмотря на то, что TypeScript значительно ускоряет адаптацию к изменениям и делает рефакторинг более доступным, этого недостаточно. Необходимо внедрение архитектурной изоляции, чтобы логика приложения оставалась независимой от мелких деталей реализации.
Выйти из капкана деталей за счет увеличения "идеальности"
Есть теория, которая гласит, что технические решения, в том числе и UI-приложения, подчиняются определенным закономерностям. Один из главных законов — стремление к повышению степени идеальности. То есть, части системы перестают быть привязанными к конкретике и могут быть реализованы вне жесткой связки с ней.
Эта теория — ТРИЗ (Теория решения изобретательских задач). В ее рамках идеальным решением считается такое, при котором часть системы выполняет свою функцию и сама как бы исчезает.
Рассмотрим пример. Взять стеклоочистители в автомобилях. Раньше водитель сам протирал лобовое стекло, потом появились механические дворники с ручным управлением, а затем — автоматические, которые работают по таймеру.
Но настоящий идеал – когда стекло само остаётся чистым без движущихся элементов. Функция есть, а механизма нет. Гидрофобное покрытие, которое заставляет воду скатываться, является отличным примером: механизм исчезает, а функция выполняется даже лучше.
"Идеальность" для UI-разработки
Этот же принцип можно перенести в UI-приложения, где мы повышаем степень идеальности:
Заменить сложные зависимости абстракциями, которые позволят менять реализацию, без переделывания всего кода.
Применить декларативный подход: описать, чего мы хотим добиться, а система пусть сама разбирается с деталями.
Чем меньше конкретных деталей и привязок внутри системы, тем более она гибкая, лёгкая и способная адаптироваться к изменениям.
Концепции этой статьи будут рассмотрены на примере реализации игры в крестики-нолики из документации React, но измененной нами.
1. Обобщение и абстракция
Обобщение и абстракция — это то, чем занимается архитектура. Она позволяет сделать части независимыми, легкотестируемыми и отложить принятие решений о деталях как можно дальше в будущее.
Истинная архитектура приложения — это про четкое разделение ответственности, в противовес жесткой привязки к определенному фреймворку.
Ключевые принципы:
Компонент как самостоятельная единица
Каждый UI-компонент (например, Game, Board, Square) должен иметь ясные задачи и быть частью общего корпоративного языка. Названия компонентов задаются на этапе дизайна (ниже объясню почему). Они не придумываются разработчиком.
## Пример онтологии: сущности находятся в отношении "is-a"
## То есть предок (parent) может заменить потомка (child) без потери корректности
Entity
├── Game
├── Square
├── Mark
├── Player
└── Board
GameSubject
├── Game
├── SquareSubject
│ ├── Mark
│ └── Move
├── PlayerSubject
│ └── Player
└── BoardSubject
└── Board
ViewModel через пропсы
Если передавать все данные через пропсы, чтобы компоненты «не знали» о внутренней логике, то такой превращает пропсы в своего рода ViewModel
, который позволяет независимо менять представление и бизнес-логику.
// Компонент App: получает состояние и передаёт его в Game через пропсы
export default function App() {
const [state, setState] = useState<TState>(...);
return <Game boardProps={mapStateToBoardProps(() => state, setState)} />;
}
// Компонент Board: отображает ряды квадратов, не вникая в детали управления состоянием
export function Board({ squareProps, status }: TBoardProps) {
return (
<>
<div className="status">{status}</div>
{squareProps.map((row, index) => (
<div key={index} className="board-row">...</div>
))}
</>
);
}
Инъекция зависимостей
Позволяет выносить операции ввода-вывода (например, запросы к серверу) в отдельный слой, чтобы упростить тестирование и заменить реализацию. А самое главное, позволяет избежать принятие решений о том, как именно мы будем получать данные на ранней стадии разработки.
// Преобразуем состояние игры в пропсы для квадратов, сгруппировав их по рядам
export const getMapStateToSquareProps =
({ someAsyncDependency }: { someAsyncDependency: () => Promise<void> }) =>
(
getState: () => TState,
setState: (state: TState) => void
): TSquareProps[][] => {
// Создаем массив рядов для квадратов
const rows: TSquareProps[][] = [];
// Для каждого квадрата создаем пропсы
setRow((i) => {
return {
value: state.squares[i],
onSquareClick: async () => {
try {
await someAsyncDependency(); // вызываем асинхронную функцию
makeMove(i, state, setState); // выполняем основной ход
} catch (error) {
console.error(error);
}
},
index: i,
};
});
return rows;
};
const mapStateToSquareProps = getMapStateToSquareProps({
someAsyncDependency: async () => {
await new Promise((resolve) => setTimeout(resolve, 500)); // имитируем задержку
alert("Async dependency has been resolved");
},
});
2. Повышение уровня декларативности
Тренд к повышению идеальности проявляется в усилении декларативного подхода. Главное — четко сформулировать, чего мы хотим достичь, а не зацикливаться на механизмах реализации, что позволяет отложить принятие решений до более поздних этапов.
Таким образом, предпочтительна стратегия реализации сверху вниз: мы постепенно выявляем сущности в процессе создания макетов и проведения экспериментов. Нет смысла продумывать все детали заранее: нужные элементы естественно сформируются во время итеративного тестирования и доработки.
В этом процессе и вырабатывается онтология сущностей, или универсальный язык, который позволяет нам общаться и согласовывать свои мысли. Это и есть декларативное описание того, что мы хотим достичь.
Онтологии
От идеи к сущности. Начиная с макетов и моков, которые описывают взаимодействие пользователя с приложением, можно постепенно вырабатывать сущности (например, Square, Move, Player) не плодя их заранее.
Автоматизация изменений. Использование инструментов вроде языка онтологий OWL, чтобы формализовать концепции и отношения между ними, может позволить AI автоматически рефакторить и упрощать структуру этой же онтологии, когда изменятся бизнес-требования.
Такой подход поможет избежать создания лишних сущностей и держать систему простой и гибкой. Это позволит изменить детали реализации, не переделывая всю архитектуру.
Дизайн
Дизайн тоже следует декларативному подходу. Макет в Figma описывает, как должна выглядеть страница, где разместить кнопки и другие элементы. Можно добавить описания: какую функцию выполняет кнопка, какие данные нужны или даже пример кода. Если попросить AI сгенерировать код, он сможет это сделать, опираясь на такое описание и данные внутри макета.
Взгляд в будущее: UI-разработка без привязки к фреймворкам
Как уже было сказано, технические решения, в том числе и UI-приложения, подчиняются определенным закономерностям, и один из главных их законов — это стремление к повышению степени идеальности. То есть, детали уходят на второй план, а обобщения на первый.
Хочу предположить и пофантазировать, что в будущем роль UI-разработчика будет не только «писать код», а скорее «строить систему идей», где:
Инструменты будут на базе онтологий. Код будет генерироваться автоматически из декларативных описаний, а онтология послужит единым источником правды.
AI в роли ассистента. Искусственный интеллект поможет не только в рефакторинге, но и в реорганизации и упрощении архитектуры.
Независимость от фреймворков. UI-код будет опираться на структуру дизайна, а дизайн — на онтологии. Это создаст гибкий конвейер, где изменения легко распространяются по всей системе.
Представьте ситуацию: дизайнер обновляет макет в Figma и находит несоответствие с текущей онтологией. В этом случае AI может помочь минимизировать изменения в общей онтологии и макете, предлагая конкретные корректировки и указывая, где дизайн можно упростить. После утверждения макета, AI автоматически зарефакторит или добавит необходимые компоненты, а бэкенд адаптируется к новым сущностям. Такой конвейер мог бы сделать процесс разработки более целостным и независимым от конкретных технологий.
Итоги
Долгосрочное планирование в UI-разработке уже не заключается в попытке предсказать будущее технологий. Речь идет о том, чтобы создать систему, которая готова меняться. Мы сможем выйти из замкнутого круга постоянных обновлений и переделок, если отделим логику приложения от конкретных деталей реализации и будем использовать формализованные онтологии.
Давайте перестанем писать код, ориентируясь исключительно на текущий тренд (React, Angular и т.д.), и начнем создавать гибкие, легко адаптируемые системы, которые выдержат испытание временем. Например, для начала, будем использовать инъекцию зависимостей, и изолированный вью. А как дополнительное упражнение, можно попробовать использовать MVVM-архитектуру, онтологии и AI для автоматизации рефакторинга и упрощения архитектуры.
Мне кажется, что в будущем UI-разработка станет больше похожа на философию в действии с элементами DevOps — а это, по сути, про понимание и интеграцию всех деталей вместе. Позволю себе вольность и скажу, что вероятно UI разработчик будет больше как "DesignOps", больше похожим на дизайнера на стероидах. Но дизайнер в моем понимании будет больше похож на архитектора (дизайн и архитектура синонимы в английском), чьей задачей является problem solving через дизайн, а не простое создание красивых макетов.
Надеюсь, эта статья была полезной и помогла вам взглянуть на разработку интерфейсов с новой стороны и ментально подготовиться к переменам, которые уже не за горами. Желаю вам и вашим приложениям прожить долгую и счастливую жизнь!