Почему «просто добавить функцию» иногда означает переделать полсистемы
Стоимость новых функций
«Добавьте ещё одну функцию».
На уровне бизнес-задачи это может звучать совершенно безобидно. Нужно добавить новый статус заказа, разрешить ещё один способ оплаты, подключить дополнительный канал продаж или дать определённым сотрудникам новый сценарий работы.
Кажется, что речь идёт об одной небольшой доработке.
Но в работающей информационной системе почти не бывает изменений, которые существуют изолированно.
Новый статус может затронуть базу данных, API, бизнес-логику, интерфейс, integration с ERP, уведомления, отчётность и права доступа. Новый способ оплаты — финансовый контур, возвраты, сверку платежей, договоры с провайдерами и бухгалтерскую отчётность. Новый тип клиента может повлиять на расчёт цены, CRM, документы и правила маршрутизации.
В результате задача, которая выглядит как «добавить одно поле», превращается в изменение нескольких связанных компонентов.
И чем дольше система развивается без пересмотра архитектуры, тем выше вероятность, что очередная небольшая функция затронет гораздо больше, чем предполагалось.
Это не обязательно означает, что система плохо написана.
> Чаще проблема в другом: требования бизнеса растут быстрее, чем архитектура успевает адаптироваться к этим изменениям.
Программный продукт меняется не по одному месту
Представим простую систему заказов.
В ней есть: заказ → клиент → товар → оплата → доставка
На раннем этапе всё может быть достаточно просто. Затем бизнес начинает развиваться.
Появляются:
- › несколько типов клиентов;
- › разные способы оплаты;
- › возвраты;
- › частичные отгрузки;
- › несколько складов;
- › программы лояльности;
- › интеграция с ERP;
- › мобильное приложение;
- › личный кабинет;
- › новые каналы продаж.
Каждое требование само по себе кажется небольшим.
Но система уже состоит из множества взаимосвязанных решений.
Изменение не обязательно большое.
Большим становится количество зависимостей, которые нужно сохранить после этого изменения.
Именно это принципиально отличает зрелую работающую систему от нового проекта, где ещё можно относительно свободно менять архитектуру.
Почему в новой системе изменить что-то проще
Когда продукт только создаётся, архитектура ещё не связана историческими решениями.
Разработчики могут свободно определить:
- › структуру данных;
- › границы модулей;
- › интерфейсы между компонентами;
- › правила доступа;
- › формат API;
- › способы обработки ошибок;
- › механизмы интеграции.
Но по мере эксплуатации система накапливает решения.
- › Некоторые из них были приняты осознанно.
- › Другие появились как временные.
- › Третьи были оптимальными несколько лет назад, но перестали соответствовать текущим требованиям.
- › Четвёртые возникли из-за ограничений внешних систем.
- › Пятые появились просто потому, что бизнесу нужно было выпустить функцию вчера.
Постепенно архитектура начинает отражать не только первоначальный замысел, но и всю историю развития продукта.
> И именно здесь возникает технический долг.
Технический долг — это не просто «плохой код»
Термин часто используют слишком широко. Иногда техническим долгом называют любой старый код, который не нравится разработчикам. Это неточно.
Компания может сознательно выбрать более быстрый и менее универсальный вариант, чтобы быстрее вывести продукт на рынок. Например, вместо полноценной архитектуры скидок реализовать несколько конкретных правил. В момент запуска это может быть абсолютно рациональным решением.
Проблема появляется позже, когда правил становится двадцать, они начинают пересекаться, а новая акция требует изменения сразу нескольких частей системы.
Первоначально сэкономленное время превращается в будущую стоимость изменений.
Поэтому технический долг — не обязательно ошибка.
Это обязательство, которое система берёт на себя ради текущей выгоды.
Проблемой он становится тогда, когда компания перестаёт контролировать размер и последствия этого обязательства.
Почему стоимость изменений растёт
Есть важное наблюдение: стоимость функции не определяется только количеством кода.
На неё влияет количество системных ограничений, которые необходимо учесть.
На ранней стадии большая часть этой формулы может быть небольшой.
В зрелой системе резко увеличивается другая часть — стоимость проверки того, что изменение ничего не сломало вокруг себя.
Например, изменение поля status может потребовать проверить:
- › бизнес-логику;
- › API;
- › мобильное приложение;
- › отчёты;
- › интеграцию с CRM;
- › интеграцию с ERP;
- › уведомления;
- › права доступа;
- › фоновые задачи;
- › исторические данные.
Чем больше связей, тем дороже изменение
Представим компонент, который используется только одним сервисом. Изменить его относительно просто.
Теперь представим компонент, которым пользуются:
- › пять внутренних сервисов;
- › мобильное приложение;
- › внешний API;
- › интеграция с ERP;
- › аналитический контур.
Любое изменение его поведения требует учитывать всех потребителей.
Получается принцип:
чем больше зависимостей у компонента, тем выше стоимость изменения этого компонента.
Именно поэтому архитектура — это не только вопрос производительности или красоты кода. Она напрямую влияет на экономику развития продукта.
Самая опасная зависимость — скрытая
Явную зависимость обычно обнаружить несложно.
Например, сервис A вызывает API сервиса B. Это можно увидеть в архитектурной документации, коде или конфигурации.
Гораздо опаснее скрытая зависимость.
- › другой сервис не вызывает API напрямую, но рассчитывает на определённый формат данных в общей таблице;
- › отчёт предполагает, что определённый статус заказа означает конкретное бизнес-состояние;
- › внешняя система ожидает, что поле никогда не будет пустым.
Формально компоненты могут выглядеть независимыми.
> Фактически они связаны контрактом, который нигде не описан.
Именно такие связи особенно сильно усложняют развитие системы.
Почему «одно поле» иногда становится большой задачей
Рассмотрим типичный пример.
Бизнес говорит:
В базе данных добавить значение относительно просто. Но что дальше?
Нужно определить:
- › кто может установить этот статус;
- › из каких состояний в него можно перейти;
- › в какие состояния можно перейти после него;
- › что происходит с оплатой;
- › нужно ли уведомлять клиента;
- › появляется ли задача у сотрудника;
- › что показывает CRM;
- › что передаётся в ERP;
- › учитывается ли заказ в отчётах;
- › можно ли отгрузить такой заказ;
- › что увидит мобильное приложение;
- › как обрабатываются старые заказы.
Получается уже не изменение поля.
Это изменение состояния бизнес-процесса.
Добавилось одно состояние.
Но изменился граф переходов, а значит, изменился сам процесс.
Архитектурные ограничения появляются постепенно
Система редко просыпается однажды утром с полностью неправильной архитектурой.
Обычно ограничения накапливаются.
Сначала появляется небольшое исключение. Потом второе. Потом третье. Затем оказывается, что новый функционал приходится реализовывать через специальные условия.
Каждое отдельное решение может казаться разумным.
Проблема возникает на уровне их комбинации.
Архитектура может перестать соответствовать бизнесу
Особенно часто это происходит с продуктами, которые начинались как относительно простые. Компания могла стартовать с одной моделью бизнеса.
Через несколько лет она уже работает с:
- › несколькими рынками;
- › разными типами клиентов;
- › несколькими юридическими лицами;
- › несколькими каналами продаж;
- › сложными тарифами;
- › партнёрами;
- › внешними API;
- › большим количеством интеграций.
А внутри продолжает жить архитектура, рассчитанная на первоначальный сценарий.
Тогда разработчики вынуждены постоянно добавлять исключения.
Это важный сигнал:
> если новый бизнес-процесс приходится постоянно «приспосабливать» под старую архитектуру, возможно, ограничением уже является сама архитектура.
Не всякий технический долг нужно погашать
Здесь тоже легко впасть в крайность. Увидеть старый код и решить «сначала всё перепишем» — не обязательно хорошая стратегия.
Полная переписывание системы редко является целью само по себе.
У технического долга должна быть экономическая оценка. Стоит спросить:
- › Как часто этот участок меняется?
Если компонент практически не меняется, его несовершенная архитектура может не создавать существенных затрат. - › Сколько изменений он блокирует?
Если один старый компонент мешает развитию нескольких ключевых функций, его приоритет значительно выше. - › Насколько дорого каждое изменение?
Если каждое новое требование требует недельного анализа и тестирования, это уже заметный бизнес-фактор. - › Какой риск несёт существующее решение?
Иногда проблема не в скорости разработки, а в надёжности, безопасности или невозможности масштабирования. - › Есть ли понятный момент для изменения архитектуры?
Иногда правильнее не делать отдельный большой проект, а менять проблемный участок постепенно вместе с развитием функциональности.
Когда «ещё одна заплатка» становится опасной
Допустим, команда знает, что определённая часть системы архитектурно устарела. Но каждый новый запрос решается небольшим обходным механизмом.
Это кажется дешевле. И иногда действительно дешевле.
Проблема начинается, когда временное решение становится постоянным.
Через несколько итераций появляется слой условий:
- › если клиент такого типа — делаем так;
- › если заказ пришёл из этого канала — делаем иначе;
- › если старая интеграция — используем отдельный сценарий;
- › если дата раньше определённого момента — применяем старое правило.
Система продолжает работать.
Но стоимость понимания этой системы начинает расти.
А это тоже технический долг.
Самый дорогой ресурс — иногда не разработка
В зрелом enterprise-продукте значительная часть стоимости изменения может приходиться не на написание кода.
Необходимо:
- › понять существующую архитектуру;
- › найти зависимости;
- › проверить контракты;
- › согласовать изменение;
- › подготовить миграцию;
- › обновить интеграции;
- › провести регрессионное тестирование;
- › проверить данные;
- › организовать релиз;
- › проконтролировать работу после внедрения.
Поэтому две системы с одинаковым объёмом кода могут иметь совершенно разную стоимость развития.
Во второй любое изменение становится расследованием.
Как понять, что architecture уже начинает мешать
Есть несколько характерных признаков.
- › Новая функция регулярно затрагивает несколько несвязанных модулей:
Если практически любое изменение требует правок в пяти-десяти местах, стоит проверить границы компонентов. - › Разработчики боятся менять старый код:
Не потому, что код старый, а потому, что неизвестно, что от него зависит. - › Регрессионное тестирование растёт быстрее функциональности:
Каждая новая функция требует всё большего набора проверок. - › Появляются специальные исключения:
Вместо общей бизнес-логики постоянно добавляются условия для отдельных клиентов, каналов или сценариев. - › Интеграции становятся хрупкими:
Изменение одного внутреннего объекта неожиданно ломает внешний обмен. - › В компании есть «незаменимые» специалисты:
Если только один человек знает, почему определённый компонент работает именно так, это не только кадровый риск. Это архитектурный сигнал.
> Разработка всё чаще начинается с фразы «там лучше не трогать» — это, пожалуй, один из самых характерных симптомов.
Архитектура должна выдерживать изменения
Хорошая архитектура — не та, которая идеально предсказывает будущее.
Предсказать все будущие требования невозможно.
Задача архитектуры другая:
— сделать наиболее вероятные изменения относительно дешёвыми и локальными.
- › Если изменение каталога товаров не должно затрагивать механизм авторизации, эти части системы желательно разделить.
- › Если новый канал продаж не должен переписывать финансовый контур, между ними должен существовать понятный контракт.
- › Если изменение внутренней реализации сервиса не должно ломать десятки клиентов API, интерфейс сервиса должен быть отделён от его внутреннего устройства.
Это и есть одна из главных задач архитектуры — управлять стоимостью изменений.
Слабая связанность — не абстрактная теория
Термин «слабая связанность» иногда звучит как академическая архитектурная теория.
На практике это очень конкретная экономическая характеристика.
Если изменение компонента A требует обязательно менять B, C, D и E, система сильно связана.
Если A можно изменить независимо, потому что взаимодействие с другими компонентами проходит через стабильные контракты, связанность ниже.
Во втором случае внутреннее изменение одного компонента не обязательно требует изменения всех остальных.
Но слабая связанность тоже имеет цену: нужны хорошие интерфейсы, контракты, обработка ошибок, версионирование и дисциплина взаимодействия.
Архитектура не убирает сложность.
— Она переносит её в управляемое место.
Почему интеграции особенно быстро увеличивают стоимость изменений
Каждая внешняя система добавляет контракт.
один формат.
другой.
третий.
Мобильное приложение использует API.
Партнёр подключён к ещё одному интерфейсу.
Теперь изменение внутренней модели данных нельзя рассматривать только внутри одного приложения.
> Нужно проверить, какие внешние потребители от неё зависят.
Поэтому при росте количества интеграций особенно важны:
- › стабильные API;
- › версионирование;
- › чёткие контракты;
- › преобразование данных на границах систем;
- › обработка ошибок;
- › мониторинг;
- › обратная совместимость.
Без этого каждая новая интеграция увеличивает не только функциональность системы, но и стоимость последующих изменений.
Иногда проблему нужно решать не кодом, а изменением границ
Это важный момент для больших систем.
Если определённый модуль постоянно меняется независимо от остальных, возможно, он должен иметь более чёткую границу. Например:
- › каталог товаров может развиваться отдельно от расчёта платежей;
- › Управление клиентами — отдельно от логистики;
- › Уведомления — отдельно от основного бизнес-процесса.
Но выделять отдельный сервис только потому, что «микросервисы — это современно», тоже неправильно.
Каждая дополнительная граница создаёт собственную сложность:
- › сетевые вызовы;
- › отказоустойчивость;
- › мониторинг;
- › согласованность данных;
- › управление версиями;
- › диагностику;
- › эксплуатацию.
Поэтому архитектурное разделение оправдано не количеством компонентов, а характером изменений и ответственностью за бизнес-функции.
Что делать, когда требования уже обогнали архитектуру
Первый шаг — не переписывать всё.
Сначала нужно понять, где именно находится ограничение. Полезно построить карту:
После этого можно увидеть участки, которые:
- › меняются чаще всего;
- › имеют слишком много зависимостей;
- › блокируют развитие;
- › содержат большое количество исключений;
- › создают высокий операционный риск.
Именно они обычно заслуживают архитектурного внимания в первую очередь.
Рефакторинг и переписывание — не одно и то же
Если архитектурный участок действительно мешает развитию, это не означает, что его необходимо удалить и написать заново.
Часто безопаснее идти постепенно.
Например:
Такой подход позволяет менять архитектуру одновременно с развитием продукта.
Он сложнее, чем «сделать новую систему с нуля», но существенно снижает риск остановить бизнес-процессы.
Для работающего enterprise-продукта это часто критически важно.
Стоимость изменения можно контролировать
Нельзя сделать систему, в которой любое будущее изменение будет дешёвым.
Но можно сделать так, чтобы стоимость изменений росла предсказуемо, а не скачкообразно.
Для этого архитектуру необходимо регулярно проверять по нескольким направлениям:
- › Изменяемость.
Какие компоненты меняются чаще всего? - › Связанность.
Сколько других компонентов приходится затрагивать? - › Контракты.
Насколько безопасно менять внутреннюю реализацию? - › Данные.
Можно ли изменить модель без разрушительных миграций? - › Интеграции.
Есть ли стабильные интерфейсы между системами? - › Тестируемость.
Можно ли быстро доказать, что изменение не сломало существующее поведение? - › Наблюдаемость.
Можно ли понять, что произошло после релиза?
Это уже не просто технические характеристики.
Это показатели того, насколько бизнес способен развивать свой цифровой продукт без постоянного увеличения стоимости изменений.
Главное — не путать рост продукта с деградацией архитектуры
Большая система не обязана быть плохой.
Сложный бизнес закономерно приводит к сложной информационной системе.
Проблема начинается не тогда, когда в системе много компонентов.
> Проблема начинается, когда её сложность перестаёт соответствовать структуре бизнеса и становится препятствием для изменений.
Поэтому фраза «нам нужно добавить ещё одну функцию» должна восприниматься не как вопрос количества кода.
Нужно понимать:
- › что именно меняется в бизнес-процессе;
- › какие компоненты отвечают за этот процесс;
- › какие зависимости существуют;
- › какие контракты затрагиваются;
- › какие данные изменятся;
- › и насколько локальным может остаться изменение.
Если функция действительно локальна — её и нужно делать локально.
Если она затрагивает фундаментальную модель продукта, лучше признать это на этапе проектирования, чем маскировать архитектурную проблему ещё одним исключением.
Потому что технический долг редко становится дорогим в момент своего появления.
Он становится дорогим тогда, когда каждое следующее изменение начинает платить за решения, принятые раньше.
Именно поэтому архитектуру стоит оценивать не только по тому, насколько хорошо система работает сегодня.
Не менее важный вопрос:
— «сколько будет стоить изменить её завтра?»
API, Webhook или очередь сообщений?
Когда API есть, а интеграция всё равно не работает
Как соединить производство и корпоративные системы
Все инженерные материалы и экспертные статьи
Полный алфавитный справочник ИТ-терминов
🔒 Данные и доступ к системам заказчика обрабатываются в соответствии с согласованными требованиями к безопасности и конфиденциальности. Условия доступа, хранения и обработки данных определяются архитектурой проекта и договором.
Сайт носит исключительно информационный характер и не является публичной офертой в соответствии со статьёй 437 Гражданского кодекса РФ. Цены, состав и условия предоставления услуг уточняются при проектировании решения и фиксируются в договоре. Условия технического сопровождения и уровень сервиса могут дополнительно определяться отдельным соглашением (SLA).
Автоматизация бизнеса и ИИ-решения под ключ
Самозанятый Скопец Антон Викторович ИНН 741709260870