Главная / Статьи / Десять архитектурных привычек, которые позволяют кодовым базам фронтенда оставаться поддерживаемыми в течение многих лет.

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

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

3870 слов

Любая кодовая база фронтенда, существующая достаточно долго, в конечном итоге разделяется на две отдельные области.

Первую можно назвать Зоной опасности.

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

Никто не хочет туда приближаться.

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

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

А есть ещё вторая область.

Назовите её Стабильной основой.

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

Они почти никогда не вызывают простоев.

В их структуре нет ничего особенно изощрённого.

Когда в команду приходит новый сотрудник, он может открыть один из этих файлов, понять его функции без пояснений и отправить первый pull request в течение дня-двух.

Именно это стоит отметить.

Код, который сохраняется, обычно не является самым совершенным в системе.

Обычно это самый простой код.

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

Изменяются требования.

Команды реорганизуются.

Заменяются зависимости.

Фреймворки развиваются дальше.

Компании меняют стратегию.

Люди уходят из компании.

Появляются новые инженеры, которые совсем не понимают, почему что-то было создано определенным образом.

Поэтому важный вопрос заключается не в следующем:

"Насколько чистым выглядит этот дизайн сейчас?"

А вот в чем:

"Каковы будут затраты на изменение этого через пять лет?"

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

1. Оптимизируйте под возможность удаления, а не повторного использования

Многие рекомендации по архитектуре сосредоточены на повторном использовании.

Сделайте свои компоненты пригодными к повторному использованию.

Создавайте универсальные сервисы.

Добавляйте точки расширения.

Проектируйте архитектуры плагинов.

Пишите абстракции для реализаций, которые вы еще не создали.

Повторное использование имеет свое место.

Но существует ещё одно качество, которое часто имеет большее значение в продукте, постоянно развивающемся:

Насколько легко что-то можно удалить.

Функции не являются постоянными.

Их заменяют.

Их перестраивают.

Они объединяются с другими функциями.

Иногда компания просто теряет к ним интерес.

Представьте проект на React, организованный строго по типам файлов:

src/
  components/
    BillingTable.tsx
    UserModal.tsx
    SubscriptionCard.tsx
  hooks/
    useBillingData.ts
    useUserData.ts
    useSubscription.ts  services/
    billingApi.ts
    userApi.ts
    subscriptionApi.ts

На первый взгляд это кажется аккуратным.

У каждого типа файла есть своя отдельная папка.

Но предположим, что через восемнадцать месяцев компания решит полностью отказаться от процесса выставления счетов.

Где же на самом деле находится всё, связанное с выставлением счетов?

Придётся искать в нескольких разных каталогах.

Вы находите и удаляете файл BillingTable.tsx.

Затем обнаруживаете файл useBillingData.ts.

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

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

Затем — таблица стилей.

Затем — вызов API.

Затем — тестовый фикстчер.

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

Эта функциональность убрана из продукта, но её остатки по-прежнему разбросаны по всему кодовому базису.

Именно так со временем накапливается «мертвый» код.

Организация по функциональным блокам делает границы гораздо более очевидными:

src/
  features/
    billing/
      components/
        BillingTable.tsx
      hooks/
        useBillingData.ts
      services/
        billingApi.ts
      types.ts
      index.ts

Теперь обработка платежей имеет четкое место хранения.

Если компания решит убрать эту функциональность, первый шаг будет простым:

src/features/billing/

Удалить папку.

Тогда TypeScript выявит все остальные элементы, которые всё ещё от неё зависят.

С такой формой зависимостей гораздо проще справляться.

Возможность удаления — это проявление поддерживаемости кода

Модуль становится проще в обслуживании, когда сразу ясно, где находятся его функции.

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

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

Цель — иметь возможность быстро ответить на следующий вопрос:

«Если эта функция исчезнет завтра, что мне потребуется удалить?»

Если на этот вопрос трудно ответить, границы функций, скорее всего, слишком расплывчаты.

2. Не превращайте папку shared/ в корзину для мусора

Существует ещё одна ловушка, которая часто возникает, когда команды переходят на архитектуру, основанную на функциях.

Всё, что явно не относится к какой-либо конкретной функции, попадает в папку shared/.

Через несколько месяцев у вас получается что-то вроде:

shared/
  utils/
  helpers/
  common/
  services/
  components/
  hooks/
  types/

И к тому моменту shared/ уже составляет половину всего кодового базиса.

Это создаёт свои собственные проблемы с взаимосвязью компонентов.

Хорошее руководство к действию:

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

Универсальный компонент кнопки естественно вписывается в систему дизайна.

Клиент для аутентификации разумно может находиться в слое общей инфраструктуры.

Вспомогательный инструмент для форматирования дат тоже можно сделать общим.

Но функция вроде:

calculateEnterpriseRenewalDiscount()

почти наверняка принадлежит той функции, которая использует соответствующее бизнес-правило.

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

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

Чем больше растет папка shared/, тем сложнее понять, кто на самом деле отвечает за определённое поведение.

3. Создавайте защитные адаптеры вокруг внешних зависимостей

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

Сегодня вы можете использовать Axios, а завтра перейти на встроенную функцию fetch.

Сейчас вы можете пользоваться одним поставщиком аналитики, а через несколько лет ваша компания может перейти на другого.

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

Хрупкий способ создания приложений — это прямое импортирование этих внешних пакетов в десятки компонентов.

import axios from 'axios';
import { trackMixpanelEvent } from 'mixpanel-browser';
export function CheckoutCard() {
  const handlePurchase = async () => {
    await axios.post('/api/checkout', payload);    trackMixpanelEvent('checkout_completed');
  };
}

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

Слой границ обеспечивает возможность замены зависимостей. Например:

// src/shared/lib/analytics.ts
import mixpanel from 'mixpanel-browser';export const analytics = {
  trackCheckoutCompleted(
    orderId: string,
    amount: number
  ) {
    mixpanel.track('checkout_completed', {
      orderId,
      amount,
    });
  },
};

Теперь компонент взаимодействует с концепцией, определенной собственным приложением:

analytics.trackCheckoutCompleted(orderId, amount);

Он не имеет ни малейшего представления о том, используется ли в качестве основы Mixpanel, PostHog, Segment или какой-то другой инструмент. Если меняется поставщик, контракт, от которого зависит приложение, может оставаться абсолютно прежним.

Но не абстрагируйте всё

Этот момент имеет такое же значение, как и предыдущий.

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

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

«Будет ли замена этой зависимости позже дорогостоящей, или будет ли риском её неконтролируемое распространение по всей кодовой базе?»

Если ответ «да», то инвестиции в адаптер оправданы. Если нет, то прямое обращение к зависимости, скорее всего, будет проще и подойдёт.

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

4. Явный поток данных лучше магии

Один из самых быстрых способов сделать кодовую базу запутанной — это скрыть источник значений.

Глобальные эмиттеры событий — это типичный пример.

eventBus.emit('USER_UPDATED', {
  id: user.id,
});

Эта строка сообщает о том, что событие было вызвано. В ней не говорится, кто за ним наблюдает.

Вы можете просмотреть всю кодовую базу и в конце концов найти что-то вроде:

eventBus.on('USER_UPDATED', handler);

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

Теперь сравните это с контрактом, в котором всё четко описано:

interface UserCardProps {
  user: User;
  onUserRoleChange: (
    userId: string,
    newRole: Role
  ) => Promise<void>;
}

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

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

Когда кто-то, не знакомый с компонентом, открывает файл, он должен иметь возможность ответить на три вопроса, не изучая остальную часть репозитория:

Откуда берутся эти данные?

Как правило, это props, параметры маршрута, хук или какой-то четко определенный слой доступа к данным.

Что может их изменить?

Видимый вызов функции, мутация, действие или явная обновление состояния.

Что происходит, когда пользователь выполняет это действие?

Прямой вызов функции, реализация которой доступна для отслеживания.

Чем меньше логики вы скрываете, тем проще становится понимание всей системы.

5. Храните бизнес-логику вне жизненных циклов фреймворка

Фреймворки — это не постоянные элементы инфраструктуры; это одно из наиболее надежных предположений при работе с фронтендом.

Уже один React претерпел несколько крупных изменений. Классовые компоненты утратили популярность. Хуки изменили способ структурирования логики с состоянием. Во многих проектах Create React App был заменен инструментами вроде Vite или встроенными решениями фреймворка. Рендеринг с сервера и более современные подходы к маршрутизации изменили способ, которым команды подходят к получению данных и определению границ приложения.

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

Возьмем в качестве примера расчёт налогов. В хрупкой версии реальная логика скрыта внутри хука React:

export function useTaxCalculator(
  cartItems: CartItem[]
) {
  const [tax, setTax] = useState(0);
  useEffect(() => {
    let calculated = 0;    // 60 lines of tax calculation,
    // rounding rules,
    // country logic,
    // exemptions...    setTax(calculated);
  }, [cartItems]);  return tax;
}

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

Лучший подход — разделить эти две области:

export function calculateTax(
  cartItems: CartItem[],
  countryCode: string
): number {
  // Pure business logic
  return totalTax;
}

Затем слой React просто вызывает эту функцию:

const tax = calculateTax(cartItems, countryCode);

Благодаря такой структуре логика, которая действительно важна, совершенно не зависит от React. Она может выполняться в любой среде, её можно тестировать с помощью обычных unit-тестов. Серверный процесс может использовать её напрямую. Кроме того, она выдерживает миграцию на другую UI-фреймворковую среду без необходимости переписывания.

Фреймворки должны находиться на периферии

Полезным способом визуализации этого является слоистая диаграмма:

┌──────────────────────────────┐
│          UI Layer            │
│      React / Next.js         │
├──────────────────────────────┤
│       Application Logic      │
├──────────────────────────────┤
│        Domain Logic          │
│     Pure TypeScript          │
├──────────────────────────────┤
│       Infrastructure        │
│ APIs / DB / Vendors / SDKs   │
└──────────────────────────────┘

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

Это не означает, что каждый проект на React требует полной реализации принципов «Чистой архитектуры».

Это означает, что необходимо четко понимать, какие части кода действительно связаны с React, а какие отражают реальные бизнес-правила.

Это две разные категории, и смешивание их приводит к проблемам.

6. Избегайте превращения хуков в мини-приложения

Эта паттерн часто встречается в кодовых базах React.

Обычно всё начинается безобидно:

function useUser() {
  // fetch user
}

Затем со временем накапливаются новые требования.

function useUser() {
  // fetch user
  // loading state  // error handling  // permissions  // analytics  // transformations  // caching  // retry logic  // business rules  // notifications  // feature flags
}

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

Хуки действительно полезны.

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

Лучший подход — позволить хуку делегировать задачи более мелким, специализированным компонентам:

function useUser() {
  const user = useUserQuery();
  const permissions =
    calculatePermissions(user.data);  return {
    user: user.data,
    permissions,
    isLoading: user.isLoading,
  };
}

Благодаря такой структуре хук превращается в слой оркестрации, соединяющий различные элементы.

Теперь вся архитектура не сводится к одной функции.

Этот барьер гораздо проще поддерживать со временем.

7. Ведите записи архитектурных решений, а не бесконечные вики

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

Это потеря контекста.

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

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

"Почему мы делаем это так? Должно же существовать более простое решение."

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

В качестве примера возьмем панель управления, построенную на Server-Sent Events вместо WebSockets.

Без контекста новый разработчик может справедливо прийти к выводу:

"WebSockets — это более современный стандарт. Давайте перейдём на него."

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

Этот аргумент остаётся незаметным, если смотреть только на сам код.

Именно такой пробел и предназначены документы с описанием архитектурных решений.

Например:

# ADR 003: Use Server-Sent Events for Dashboard Feeds
## ContextOur dashboard requires real-time metric updates.We evaluated WebSockets and Server-Sent Events.## DecisionWe chose Server-Sent Events because:1. Communication is strictly server-to-client.
2. SSE uses standard HTTP infrastructure.
3. Browser reconnection is supported natively.
4. The solution works reliably within our enterprise network environment.## ConsequencesIf we later require client-to-server
bi-directional streaming, we should
re-evaluate this decision.

Благодаря такому документу следующему инженеру не нужно заново анализировать причины принятия решения.

Он может сразу увидеть почему.

Это просто

/docs/adr/

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

Документируйте решения, а не всё подряд

Вам не обязательно вести огромный вики из сотни страниц.

В большинстве случаев сам код должен быть достаточно понятным, чтобы объяснить что он делает.

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

  • почему была выбрана та или иная технология
  • почему был отклонен более очевидный вариант
  • почему существует та или иная необычная ограничение
  • почему всё ещё требуется решение, кажущееся ненужным

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

8. Проектирование для инженера, который придет после вас

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

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

Сможет ли новая команда продолжать ею управлять?

Если ваш честный ответ — нет, это не обязательно означает, что у вас не хватает разработчиков. Это означает, что система имеет скрытую зависимость от знаний конкретных людей.

Система, созданная для долговечности, должна позволять самостоятельно выявлять свои важные особенности работы.

Новый сотрудник должен иметь возможность открыть репозиторий и постепенно собрать ответы на такие вопросы:

  • Где в кодовой базе находится эта конкретная функция?
  • Какой модуль отвечает за эту функциональность?
  • Откуда на самом деле поступают эти данные?
  • На какие внешние системы опирается этот код?
  • Какие предположения делает этот код без явного указания?
  • Почему был сделан именно такой архитектурный выбор?
  • Что можно изменять без повреждения других элементов?
  • Именно поэтому четкие границы имеют такое большое значение.

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

    Сама база кода должна содержать достаточно информации об этой истории.

    9. Сокращайте радиус влияния изменений

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

    Представьте простую заявку на новую функцию: кто-то просит добавить кнопку, позволяющую пользователям экспортировать отчет о оплате в формате CSV.

    В тесно связанной системе для реализации этой функции может потребоваться изменение файлов, расположенных в разных местах:

    components/
    hooks/
    services/
    utils/
    types/
    global state/
    shared helpers/
    

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

    Сравните это с правильно структурированной системой функций:

    features/
      billing/
        components/
        hooks/
        services/
        utils/
    

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

    Именно это имеют в виду люди, когда говорят о сокращении радиуса воздействия изменений.

    Маленький радиус воздействия дает следующие преимущества:

    • Меньше регрессий
    • Проще проверка кода
    • Быстрее выпуск продукта
    • Меньше конфликтов при слиянии
    • Проще тестирование
    • Безопаснее рефакторинг

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

    10. Прекратите оптимизацию ради схемы архитектуры

    Красивая схема архитектуры может всё равно скрывать кодовую базу, с которой трудно работать.

    Вы можете отметить каждый из этих пунктов:

    • соблюдение принципов чистой архитектуры
    • использование принципов проектирования SOLID
    • правильная инверсия зависимостей
    • обёртка операций доступа к данным с помощью шаблонов репозиториев
    • создание объектов с использованием шаблонов фабрик
    • связывание компонентов с помощью событий
    • накопление нескольких уровней абстракции друг на друге

    и всё равно превратить простую функциональность в многодневную работу.

    Архитектура должна снижать сложность, а не увеличивать её. Если ваш архитектурный слой вводит больше концепций, чем сам продукт, что-то пошло не так.

    Часто самая надёжная архитектура — это та, о которой никто не старается говорить, потому что инженеры могут просто прочитать код и следовать ему. На практике это может выглядеть так:

    features/
      billing/
      checkout/
      accounts/
    

    в сочетании с простым решением:

    shared/
      ui/
      lib/
    

    Плюс несколько функций, связанных исключительно с бизнес-логикой.

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

    Что на самом деле оптимизируют старшие инженеры

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

    Менее опытный инженер может спросить:

    "Как сделать этот код повторно используемым?"

    Старший инженер вместо этого спрашивает:

    "Действительно ли он нуждается в повторном использовании?"

    Менее опытный инженер может спросить:

    ">Как мне абстрагировать этот код?"

    Старший инженер вместо этого спрашивает:

    «Какую конкретную проблему должна решить эта абстракция?»

    Менее опытный инженер может спросить:

    «Куда следует поместить эту вспомогательную функцию?»

    Опытный инженер вместо этого спрашивает:

    «Кто на самом деле отвечает за эту часть поведения?»

    Менее опытный инженер может спросить:

    «Как нам подготовиться к любым будущим требованиям?»

    Опытный инженер вместо этого спрашивает:

    «Какое будущее изменение достаточно вероятно, чтобы оправдать добавление этой сложности сейчас?»

    И, возможно, самый показательный вопрос из всех:

    «Каким будет этот код, когда человек, который его написал, уйдет?»

    Именно с этого вопроса начинается долгосрочное мышление в инженерии.

    Итог: надежный код часто выглядит обычно

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

    Основные принципы просты:

    1. Организуйте код по функциям и ответственным лицам. Функции должны быть легко находимыми, а при необходимости — легко удаляемыми.
    2. Давайте предпочтение возможности удаления кода перед максимизацией его повторного использования. Не каждый фрагмент логики заслуживает того, чтобы стать общедоступной абстракцией.
  • Защищайте свое приложение от зависимостей от сторонних поставщиков. Используйте адаптеры там, где изменения у поставщика могут повлиять на большую часть кодовой базы.
  • Давайте предпочтение явному потоку данных. Небольшой дополнительный, очевидный код обычно стоит дешевле, чем скрытое, неявное поведение.
  • Разделяйте бизнес-логику от жизненных циклов фреймворка. Задача React — отображать контент и координировать процессы, а не управлять каждым правилом, от которого зависит бизнес.
  • Сохраняйте узкий диапазон применения хуков. Не позволяйте пользовательскому хуку превращаться в отдельное небольшое приложение.
  • Фиксируйте архитектурные решения. Записывайте обоснование необычных выборов, а не только описание текущего состояния.
  • Ограничивайте зону влияния изменений. В идеале функционал может меняться без необходимости вмешательства в половину репозитория.
  • Не путайте сложность с качеством. Увеличение количества компонентов автоматически не делает архитектуру лучше.
  • Самая высокая похвала, которую может получить кодовая база, — это не:

    "Эта архитектура поразительно умна."

    А вот что это такое:

    "Я понимаю это."

    Потому что через пять лет первоначальные разработчики, скорее всего, уйдут. Фреймворк, вероятно, изменится. Дизайн тоже изменится. Продукт будет развиваться. Сам бизнес может сильно отличаться от того, что он есть сейчас.

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

    Вот как на самом деле выглядит надежное программное обеспечение.

    Связанные статьи

  • Распространённые ошибки в контракте API, разрушающие надёжность фронтенда — Узнайте о десяти часто встречающихся недостатках проектирования API-серверной части — от неоднородных форматов ответов до нестабильной пагинации — которые подрывают доверие к фронтенду, и способах их устранения.
  • Ошибки архитектуры бэкенда, мешающие командам React с фокусом на фронтенд — Рассматриваются пять типичных недостатков проектирования бэкенда в проектах на React — от неправильного использования парадигмы API до нестабильных развертываний — и архитектурные решения для обеспечения надёжности уровня продакшена.
  • Почему абстракции фронтенда незаметно превращаются в технический долг — Узнайте, почему преждевременное использование абстракций фронтенда приводит к скрытой сложности, и как определить, стоит ли вообще создавать общие компоненты, хуки или утилиты.