Содержание
Большинство программных систем не умирают из-за технологий. Они умирают из-за архитектурных решений, принятых в первые месяцы разработки.
Когда проект стартует, кажется, что главное — быстрее выпустить первую версию. Сроки давят, инвесторы ждут демо, конкуренты уже в проде. Архитектор рисует «простую» схему. Команда пишет код, который «потом отрефакторим». Через три года «потом» превращается в «никогда» — и любое изменение стоит как мини-проект.
Но настоящая стоимость системы определяется не скоростью разработки MVP. Она определяется способностью продукта адаптироваться к новым требованиям без постоянных переписываний, потери данных и месяцев простоя бизнеса.
Почему одни системы успешно развиваются десятилетиями — бухгалтерия в банке, складской учёт в ритейле, CRM в B2B — а другие превращаются в дорогостоящий технический долг уже через несколько лет? Ответ почти никогда не в «устаревшем фреймворке». Ответ — в том, насколько дёшево стоит каждое следующее изменение.
Эта статья — для архитекторов, CTO, тимлидов и senior-разработчиков, которые проектируют систему сегодня и не хотят через пять лет объяснять бизнесу, почему «добавить одну кнопку» занимает квартал.
Ключевые выводы
Системы умирают от связанности, а не от Java vs Go. Чем сильнее модули зависят друг от друга, тем дороже каждое изменение — и тем быстрее команда перестаёт успевать за бизнесом.
Проектируйте изменения, а не функции. Вопрос «как реализовать фичу X» важен на спринте. Вопрос «что будет, если через три года X изменится» — важен на десятилетие.
Домен живёт дольше технологий. Клиент, заказ, счёт, поставка — переживут смену фреймворка, БД и UI. Архитектура должна отражать предметную область, а не слои «controller / service / repository».
Универсальность — самая дорогая ловушка. Конструкторы всего, EAV-таблицы и «настраиваемые поля для всего» выигрывают на демо и проигрывают в продакшене.
Данные важнее кода. Код можно переписать за полгода. Миграция десятилетней истории заказов с кривой схемой — это годы и риск для бизнеса.
Архитектура должна пережить создателей. Люди уходят быстрее, чем меняются технологии. Понятная структура и ADR дешевле, чем «гениальные» абстракции, которые понимает один человек.
Система живёт долго не потому, что её создали идеально. Она живёт долго потому, что каждое следующее изменение остаётся достаточно дёшевым.
Введение
Архитектура — это не выбор между микросервисами и монолитом на диаграмме в Miro. Это накопленный эффект решений о границах, данных, зависимостях и компромиссах — решений, которые в момент принятия кажутся мелкими.
«Давайте пока положим статус заказа в общую таблицу entities». «Сделаем один сервис CommonService для всего». «Добавим настраиваемые поля — клиент сам настроит». Каждое такое решение экономит день на старте и забирает неделю через три года.
Долгоживущие системы объединяет одно свойство: стоимость изменения растёт медленно. Плохие системы — наоборот: после определённого порога каждая новая фича требует трогать половину кодовой базы, согласовывать с пятью командами и молиться, что ничего не сломается в prod.
Невозможно заранее угадать, какой станет система через 10 лет. Но можно построить её так, чтобы изменения не превращались в катастрофу. Именно об этом — остальные главы.
Глава 1. Почему большинство систем не доживают до 10 лет
Типичный жизненный цикл проекта
Жизненный цикл большинства корпоративных систем предсказуем. Его полезно знать не как теорию, а как чек-лист раннего предупреждения: на каком этапе вы сейчас и что будет дальше, если ничего не менять.
Этап 1. Эйфория старта
Первые месяцы — золотое время:
- Быстрая разработка. Команда из пяти человек знает весь код. Решения принимаются за обедом.
- Простая архитектура. Один репозиторий, одна БД, один деплой. Диаграмма помещается на салфетку.
- Минимум ограничений. «Сделаем правильно потом», «рефакторинг после MVP», «это временно».
На этом этапе velocity высокая, багов мало, бизнес доволен. Кажется, что так будет всегда. Это иллюзия: простота — следствие малого масштаба, а не хорошей архитектуры.
Этап 2. Рост требований
Бизнес растёт — растёт и система:
- Новые функции. Отчёты, дашборды, мобильное приложение, API для партнёров.
- Новые роли пользователей. Менеджер, бухгалтер, кладовщик, аудитор — у каждого свои права и сценарии.
- Интеграции. CRM, бухгалтерия, маркетплейсы, банки, логистика.
- Отчёты. «Нужен pivot по регионам за последние три года с группировкой по SKU».
Каждая новая возможность добавляется в существующую структуру. Если границы модулей не были заложены, новые функции просто «приклеиваются» к тому, что уже есть. Связанность растёт линейно с каждым релизом.
Этап 3. Появление первых костылей
Временные решения становятся постоянными:
- Флаг
is_legacy_flowв таблице заказов, потому что «новый процесс пока только для одного клиента». - Копипаста логики расчёта скидки в три модуля — «потом вынесем в общий сервис».
JOINчерез семь таблиц для одного отчёта — «оптимизируем, когда будет время».
Костыли копируются быстрее, чем рефакторятся. Новый разработчик не знает истории и повторяет паттерн. Через год «временное» решение — единственный способ, которым система вообще работает.
Этап 4. Архитектурный кризис
Симптомы узнаваемы:
- Любое изменение ломает соседние части. Поправили расчёт НДС — упали отчёты по складу.
- Скорость разработки падает. То, что раньше делали за спринт, теперь — за квартал.
- Растёт количество ошибок. Регрессии после каждого релиза. QA не успевает.
- Команда боится трогать код. «Не знаем, что сломается».
На этом этапе бизнес уже теряет деньги: конкуренты быстрее выводят фичи, а ваши «простые доработки» стоят как проекты.
Этап 5. Большая перепись
Классический финал:
- Решение переписать систему с нуля — «на современном стеке, правильно».
- Потеря времени и денег: два-три года параллельной разработки, двойная команда, миграция данных.
- Повторение старых ошибок — потому что те же люди под тем же давлением снова экономят на границах «до первого релиза».
Перепись редко решает проблему навсегда. Она сбрасывает таймер — и цикл начинается заново, если не изменился подход к проектированию.
Эйфория → Рост → Костыли → Кризис → Перепись → Эйфория (v2) → ...
↑______________________________________________|
(если не менять принципы)
Глава 2. Главный принцип долгоживущих систем
Проектируйте изменения, а не функции
Большинство разработчиков проектируют систему под текущие требования. Product owner приносит user story — команда реализует. Архитектура «складывается» из ответов на конкретные задачи спринта.
Но требования всегда меняются. Закон, который сегодня не касается вашего продукта, через два года станет обязательным. Клиент попросит интеграцию, о которой никто не думал. Рынок изменится — и «очевидная» бизнес-модель устареет.
Долгоживущие системы проектируют иначе: изменения должны быть дешёвыми. Не «идеальными». Не «красивыми». Дешёвыми — в смысле времени, риска и количества затронутых модулей.
Неправильный вопрос
Как реализовать эту функцию?
Этот вопрос правилен для задачи на два дня. Он опасен как единственный архитектурный вопрос. Он ведёт к решениям вида: «быстрее всего добавить поле в существующую таблицу» — без понимания, что таблица уже обслуживает четыре несвязанных домена.
Правильный вопрос
Что произойдёт, если через три года требования изменятся?
Конкретные формулировки, которые стоит задавать на design review:
| Вопрос | Зачем |
|---|---|
| Какие части системы придётся менять, если изменится правило X? | Оценка связанности |
| Можно ли заменить модуль A, не трогая модуль B? | Независимость |
| Где хранится «истина» для этой сущности? | Единый источник данных |
| Что если завтра появится второй канал продаж / вторая валюта / второй склад? | Расширяемость без переписывания |
| Как откатить это изменение? | Управляемость риска |
Хорошая архитектура — это архитектура, где типичное изменение бизнеса локализовано. Добавили новый тип документа — изменился один модуль. Изменили правило скидок — изменился один сервис. Не половина монолита.
Глава 3. Боритесь со связанностью
Что такое связанность
Связанность (coupling) — это степень, в которой изменение в одном месте системы вынуждает менять другие места.
Низкая связанность: модуль «Биллинг» меняет формат счёта — модуль «Склад» не затронут.
Высокая связанность: изменение статуса заказа требует правок в пяти сервисах, трёх триггерах БД, двух cron-задачах и фронтенде — потому что логика «размазана» по всей системе.
Связанность — главный враг долгоживущих систем. Не legacy-код. Не «старый» фреймворк. Именно связанность делает legacy неизлечимым без переписывания.
Примеры высокой связанности
Общие таблицы на всю систему
Одна таблица documents для заказов, счетов, актов, заявок и внутренних memo. Различие — в поле type. Через три года в ней 200 колонок, половина nullable, индексы не работают, а бизнес-аналитик не может объяснить, какие поля относятся к чему.
Глобальные состояния
Singleton-сервис AppContext, через который половина модулей читает «текущего пользователя», «текущую организацию», «текущие настройки». Тестировать невозможно. Параллельные запросы ломают друг друга.
Универсальные сущности
Entity + Attribute + Value — классический EAV. «Опишем всё через метаданные». На демо гибко. В prod — JOIN на JOIN, нет типизации, валидация в runtime, отчёты пишутся неделями.
Общие сервисы на все случаи жизни
CommonService, UtilsService, HelperService — свалка методов, которую используют 40 модулей. Любое изменение — риск для всех. Никто не знает, кто что вызывает.
Последствия
| Последствие | Как проявляется |
|---|---|
| Невозможность локальных изменений | «Нельзя трогать модуль X — от него зависит половина системы» |
| Рост технического долга | Костыли множатся, потому что «правильный» рефакторинг слишком дорог |
| Замедление команды | Онбординг месяцами. Code review — лотерея |
| Хрупкость | Баг в одном месте — каскад сбоев |
| Страх изменений | Команда добавляет ещё один слой обхода вместо исправления корня |
Как снижать связанность
- Явные границы модулей с контрактами (API, события, интерфейсы).
- Запрет на «удобный» доступ к чужим таблицам и внутренним классам.
- Один источник истины для каждой бизнес-сущности.
- События вместо прямых вызовов там, где модули должны реагировать, а не управлять друг другом.
Глава 4. Проектируйте вокруг бизнес-домена
Почему технические слои недолговечны
За 10 лет в типичном проекте изменится почти всё техническое:
| Слой | Что меняется |
|---|---|
| Язык программирования | PHP → Java → Go → что-то ещё |
| База данных | MySQL → PostgreSQL → распределённое хранилище |
| Фреймворк | Symfony → Laravel → NestJS → «следующий хайп» |
| UI | jQuery → React → «серверные компоненты» → неизвестно что |
| Инфраструктура | bare metal → Docker → Kubernetes → serverless |
Структура «controllers / services / repositories / models» не переживает смену команды и технологий, потому что она ничего не говорит о бизнесе. Новый разработчик видит папки — и не понимает, где заканчивается «заказ» и начинается «склад».
Бизнес-сущности живут дольше
При этом предметная область меняется медленнее:
- Клиент — был, есть и будет (пусть и с другими полями).
- Заказ — суть та же: кто, что, когда, за сколько.
- Счёт — деньги, налоги, срок оплаты.
- Поставка — откуда, куда, когда, статус.
- Сотрудник — роли, права, оргструктура.
Десять лет назад заказ оформляли по телефону. Сегодня — через приложение и маркетплейс. Заказ остался заказом. Изменились каналы и правила — не суть.
Домен живёт дольше технологий
Отсюда следует практический принцип: архитектура должна строиться вокруг предметной области, а не вокруг технических слоёв.
Плохо (технические слои): Хорошо (доменные модули):
src/ src/
controllers/ orders/
services/ OrderService
repositories/ OrderRepository
models/ OrderStatus
utils/ billing/
InvoiceService
inventory/
StockMovement
Модуль orders можно переписать с PHP на Go, не трогая billing. Модуль inventory можно вынести в отдельный сервис, когда нагрузка вырастет. Границы совпадают с языком бизнеса — и их понимают не только разработчики, но и product owner, и аналитики.
Domain-Driven Design здесь не «модная методология», а инструмент долголетия: bounded context, ubiquitous language, явные aggregate — всё это про то, чтобы код и разговоры о продукте говорили на одном языке десятилетие.
Глава 5. Никогда не делайте систему полностью универсальной
Самая дорогая ловушка
Желание предусмотреть все варианты заранее — одна из самых дорогих ошибок в корпоративной разработке. Она приходит под благовидными именами: «гибкая архитектура», «платформа», «конструктор процессов», «настройка без программистов».
На презентации это выглядит как competitive advantage. В эксплуатации — как монстр, которого некому поддерживать.
Как появляются монстры
Типичные симптомы универсальности:
| Паттерн | Обещание | Реальность через 5 лет |
|---|---|---|
| Конструкторы всего | «Любой процесс без кода» | 400 шагов в workflow, никто не понимает, почему счёт не выставился |
| Настраиваемые поля для всего | «Клиент сам добавит поле» | EAV, отчёты через VIEW, DBA в постоянном стрессе |
| Универсальные таблицы | «Одна модель для всех документов» | 180 nullable-колонок, миграции — кошмар |
| Универсальные формы | «Form builder в админке» | Валидация в runtime, баги только в prod |
| Универсальные процессы | «BPM для любого сценария» | Два параллельных процесса на один документ, race conditions |
Почему это выглядит красиво на старте
Универсальность реально экономит время в первые месяцы:
- Не нужно писать отдельную модель для каждого типа документа — «добавим тип в справочник».
- Демо впечатляет: «смотрите, за пять минут создали новую сущность».
- Продажи обещают «подходит любому бизнесу».
- Архитектор получает одобрение на «масштабируемое решение».
Это кредит под высокий процент. Проценты начисляются в запросах к БД, в слоях абстракции, в головах разработчиков — и в простое бизнеса, когда «простое изменение» занимает месяц.
Почему это становится кошмаром через несколько лет
- Производительность. Универсальные запросы не индексируются так же хорошо, как типизированные.
- Потеря типизации. Ошибки уезжают в prod — «поле было строкой, а нужно число».
- Невозможность рефакторинга. Логика размазана по метаданным, конфигам и «универсальному движку».
- Зависимость от «магов». Только два человека понимают, как работает конструктор — оба уже уволились.
- Бизнес теряет контроль. «Мы не можем изменить процесс — это сломает трёх клиентов».
Правило: 80% системы — типизированный домен с явными правилами. 20% — контролируемые точки расширения (плагины, webhooks, пользовательские поля на периферии). Не наоборот.
Подробнее о цене универсальности в ERP — в статье «Самая дорогая ошибка в архитектуре ERP».
Глава 6. Данные важнее кода
Код можно переписать
Код — это интерпретация бизнес-правил на конкретном языке и фреймворке. Его можно:
- переписать на другом языке;
- разбить на микросервисы;
- заменить AI-генерацией (шутка, но тренд понятен).
Данные — это история бизнеса. Заказы за десять лет. Платежи. Аудиторский след. Договорённости с клиентами, зафиксированные в системе.
Данные переписать почти невозможно
Миграция данных — самая дорогая и рискованная операция в жизни системы:
- Потеря данных = судебные иски, штрафы, потеря доверия.
- Некорректная миграция = отчёты не сходятся, бухгалтерия в панике.
- Долгая миграция = месяцы параллельной работы двух систем.
Плохая модель данных переживает любое количество рефакторингов кода — потому что «трогать БД страшно».
Ошибки проектирования данных
Непродуманные связи
Foreign key на «универсальную» таблицу без понимания жизненного цикла. Cascade delete, который сносит половину истории. Связь many-to-many без промежуточной сущности с метаданными.
Отсутствие истории изменений
«Просто обновим поле status». Через год никто не знает, кто и когда перевёл заказ в «отменён» — а это нужно для спора с клиентом или аудита.
Хранение бизнес-логики в данных
Флаг skip_validation = true. Магическое число в поле type = 7. JSON с правилами, которые интерпретирует «универсальный движок». Логика в данных не версионируется и не тестируется так же, как код.
Универсальные EAV-структуры
-- Красиво на диаграмме. Кошмар в SELECT.
entity_id | attribute_code | value_text | value_number | value_date
----------+----------------+------------+--------------+-----------
1001 | customer_name | ООО Рога | NULL | NULL
1001 | amount | NULL | 15000.00 | NULL
1001 | due_date | NULL | NULL | 2026-07-01
Три строки вместо одной типизированной записи. Отчёт «все заказы с суммой > 10 000» — pivot и молитва.
Как проектировать данные на десятилетие вперёд
- Модель отражает бизнес, а не UI. Таблица
orders, а неscreen_form_data. - Append-only история для критичных сущностей: audit log, event sourcing для статусов,
valid_from/valid_toдля справочников. - Явные типы и ограничения в БД — not null, check constraints, enums там, где домен стабилен.
- Стратегия миграций с первого дня — инструмент (Flyway, Liquibase, Alembic), правила именования, rollback-план.
- Разделение «операционных» и «аналитических» данных — не гонять OLAP-запросы по прод-таблицам заказов.
- Документирование инвариантов — «сумма строк заказа = total», «нельзя удалить оплаченный счёт» — в ADR и в constraints, где возможно.
Глава 7. Стройте систему из независимых модулей
Что такое настоящий модуль
Модуль — не папка в проекте.
Модуль — не namespace.
Модуль — не набор файлов с общим префиксом.
Модуль — это часть системы, которую можно менять независимо — с минимальным риском для остальных частей и с понятным контрактом наружу.
Тест на модуль: «Можем ли мы заменить реализацию модуля Billing, не перекомпилируя Orders?» Если нет — это не модули, а компоненты одного монолита с иллюзией структуры.
Признаки хорошего модуля
| Признак | Что это значит |
|---|---|
| Собственные данные | Модуль владеет своими таблицами / коллекциями. Другие читают через API, не через прямой SQL |
| Собственные правила | Бизнес-логика модуля не размазана по Utils |
| Минимум зависимостей | Зависит от контрактов, а не от реализаций соседей |
| Явный публичный API | Понятно, что можно вызывать снаружи, а что — внутреннее |
| Независимое тестирование | Модуль тестируется изолированно, с mock-зависимостями |
Признаки плохого модуля
- Общая база логики —
BaseEntity,AbstractDocument, от которого наследуется всё. - Общие таблицы — модуль «Склад» пишет в таблицы модуля «Заказы».
- Постоянные перекрёстные вызовы — A вызывает B, B вызывает C, C вызывает A.
- Shared database как интеграция — антипаттерн «integration via database».
Modular monolith — разумный компромисс
Не обязательно сразу идти в микросервисы. Modular monolith — один деплой, но жёсткие границы модулей внутри:
┌─────────────────────────────────────────────┐
│ Modular Monolith │
│ ┌─────────┐ ┌─────────┐ ┌─────────────┐ │
│ │ Orders │ │ Billing │ │ Inventory │ │
│ │ (API) │ │ (API) │ │ (API) │ │
│ └────┬────┘ └────┬────┘ └──────┬──────┘ │
│ │ events │ events │ │
│ └────────────┴──────────────┘ │
└─────────────────────────────────────────────┘
Когда модуль созреет — его можно вынести в отдельный сервис без переписывания домена. Границы уже есть.
Глава 8. Делайте архитектуру понятной для новых разработчиков
Почему люди меняются быстрее технологий
За 10 лет в типичной команде:
- Уйдёт архитектор, который «всё задумывал».
- Уйдёт тимлид, который знал все костыли.
- Поменяется команда два-три раза полностью.
- Придут джуны, которые через год станут мидлами — и будут принимать архитектурные решения.
Система должна пережить своих создателей. Если только Вася понимает, как работает модуль оплаты — это не архитектура, а bus factor = 1.
Признаки понятной архитектуры
- Очевидные правила. «Заказы не импортируют Billing напрямую — только через
BillingPort». Одно правило, одно место в документации. - Единообразные решения. Все модули устроены одинаково:
domain / application / infrastructure. Не «тут так, там иначе, потому что Петя так писал». - Простая навигация по коду. Новый человек за день находит, где создаётся заказ, где меняется статус, где генерируется счёт.
- Минимум магии. Нет framework, который «сам всё inject'ит» без понимания. Нет code generation, который никто не читает.
Стоимость «умных» решений
Сложные паттерны — не признак зрелости. Часто это признак того, что архитектор хотел показать экспертизу:
| «Умное» решение | Скрытая цена |
|---|---|
| Generic repository на всё | Потеря типизации, непонятные запросы |
| Event sourcing везде | Сложность без выигрыша для CRUD |
| CQRS для формы из трёх полей | Два хранилища, eventual consistency ради галочки |
| Plugin system на первый день | Никто не пишет плагины, все лезут в core |
Простота — не примитивность. Простая архитектура, которую понимает команда из восьми человек, переживёт «гениальную», которую понимает один.
Правило: если решение нельзя объяснить product owner за пять минут — оно слишком сложное для текущего масштаба.
Глава 9. Документируйте решения, а не код
Документация, которая действительно нужна
Код сам себя не объясняет — он объясняет что делает система. Не объясняет почему.
Через три года никто не вспомнит, почему выбрали PostgreSQL вместо MongoDB. Почему статус заказа — enum, а не строка. Почему интеграция с банком — синхронная, а не через очередь.
Бесполезно
- Комментарии к каждому методу —
// returns user by idнадgetUserById. - Описание очевидного — «этот класс представляет заказ».
- Wiki, которую никто не обновляет — устаревшая документация хуже отсутствующей.
Полезно
- Почему принято решение — контекст, ограничения, давление сроков.
- Какие альтернативы рассматривались — и почему отвергнуты.
- Какие ограничения существуют — «нельзя менять формат ID — от него зависят внешние интеграции».
- Что будет, если нарушить правило — «прямой доступ к таблице orders из модуля reports сломает блокировки».
Architecture Decision Records (ADR)
ADR — короткий документ на каждое значимое архитектурное решение:
# ADR-007: Статус заказа как enum в БД
## Статус
Принято
## Контекст
Нужна фиксация жизненного цикла заказа. Рассматривали строку,
JSON в metadata, отдельную таблицу transitions.
## Решение
Enum order_status в PostgreSQL + таблица order_status_history
для аудита.
## Последствия
+ Типизация, простые запросы
+ Явная история
- Новый статус = миграция (приемлемо: статусы меняются редко)
ADR хранятся в репозитории рядом с кодом (docs/adr/). Они версионируются, их видит каждый разработчик при onboarding. Это архитектурная память проекта — то, что переживает уход людей.
Глава 10. Проектируйте систему для команды будущего
Через 10 лет разработчики будут другими
Они не будут знать:
- Почему сделано именно так — «так исторически сложилось» не принимается.
- Какие были ограничения — «нам нужен был релиз к Black Friday 2023».
- Какие компромиссы принимались — «мы знали, что EAV — плохо, но клиент платил за демо через две недели».
Если эта информация не зафиксирована — команда будущего либо боится менять код, либо ломает систему, пытаясь «сделать правильно» без контекста.
Архитектура должна объяснять себя сама
Практические принципы:
| Принцип | Пример |
|---|---|
| Предсказуемая структура | Каждый модуль: domain/, application/, infrastructure/ |
| Минимум магии | Явная регистрация зависимостей, не «auto-scan всего» |
| Максимум прозрачности | Логирование на границах модулей, trace id сквозь запрос |
| Convention over configuration | Один способ делать вещи — не пять |
| Fail fast | Нарушение контракта — ошибка при старте, не в prod в пятницу |
Onboarding-тест: новый разработчик за две недели (не два месяца) может:
- найти место для типичной задачи;
- понять, какие модули затронет изменение;
- написать код, который пройдёт review без «у нас так не делают».
Если onboarding занимает месяцы — архитектура уже стареет, даже если код «работает».
Глава 11. Как понять, что архитектура стареет
Ранние симптомы
Архитектурное старение — не событие, а процесс. Его можно заметить по метрикам и поведению команды.
Каждый новый модуль становится сложнее предыдущего
Первый модуль — 500 строк. Пятый — 5000, потому что «нужно учесть все случаи» и «переиспользовать» полумёртвый CommonCore.
Любое изменение требует изменений в нескольких местах
User story «добавить поле в карточку клиента» превращается в задачу на 13 файлов в 4 модулях.
Время разработки растёт при том же размере команды
Velocity падает год к году. Story points растут. «Раньше делали за спринт» — «теперь за квартал».
Количество багов увеличивается
Регрессии. Hotfix'ы после каждого релиза. «Мы не трогали этот модуль» — «но он сломался».
Команда боится рефакторинга
«Не трогай — работает». «Сделаем обходной путь». «Ещё один if». Технический долг растёт, потому что погашение кажется дороже, чем новый костыль.
Что делать
- Замерять стоимость изменений — сколько файлов / модулей затрагивает типичная фича.
- Architecture fitness functions — автоматические проверки: «модуль A не импортирует модуль B напрямую».
- Регулярные architecture reviews — раз в квартал, не «когда уже горит».
- Локальный рефакторинг — не «большая перепись», а постоянное улучшение границ.
Глава 12. Практический чек-лист системы на 10 лет
Используйте как checklist на design review или при аудите существующей системы.
Архитектура
- Есть чёткие границы модулей — нарисованы, задокументированы, проверяются в CI
- Нет глобальных зависимостей — нет
GlobalContext,AppState, «удобного» доступа ко всему - Нет универсальных сущностей ради универсальности — каждая сущность оправдана доменом
- Интеграция через контракты — API, события, порты — не через shared DB
- Modular monolith или осознанные сервисы — не «распределённый монолит»
Данные
- Модель отражает бизнес — таблицы и сущности говорят на языке домена
- Есть стратегия миграций — инструмент, процесс, rollback
- Есть история изменений — audit log / event log для критичных сущностей
- Constraints в БД — не только валидация в коде
- Разделение OLTP и аналитики — или план на это
Команда
- Новый разработчик может разобраться за недели, а не месяцы
- Архитектурные решения задокументированы — ADR, README модулей
- Единые conventions — структура, naming, code review checklist
- Bus factor > 1 — критичные модули знают минимум двое
Развитие
- Изменения локализованы — типичная фича затрагивает 1–2 модуля
- Новые функции не требуют переписывания старых
- Есть метрики стоимости изменений — и они не растут бесконтрольно
- Рефакторинг — часть процесса, а не «когда-нибудь потом»
Глава 13. Что действительно делает систему долгоживущей
Не технологии
Система не живёт 10 лет потому, что выбрали:
- «правильный» язык программирования;
- «лучшую» базу данных;
- «модный» фреймворк.
Все эти вещи меняются. Системы на COBOL и Mainframe до сих пор работают — не из-за технологий, а из-за устойчивости к изменениям (или из-за того, что менять страшнее, чем терпеть).
А способность изменяться
Система живёт долго потому, что каждое следующее изменение остаётся достаточно дёшевым:
- бизнес может адаптироваться к рынку;
- команда может доставлять фичи без квартальных проектов на «одну кнопку»;
- данные сохраняются и накапливают ценность;
- новые люди могут входить в проект без полугодового обучения.
Это и есть главный критерий хорошей архитектуры. Не красота диаграммы. Не количество паттернов. Не соответствие последнему blog post от ThoughtWorks.
Стоимость типичного изменения через 5 лет должна быть сопоставима со стоимостью через 1 год — пусть и выше, но не на порядок.
Если через три года «простая» фича стоит в десять раз дороже, чем на старте — архитектура уже проиграла. Технологии тут ни при чём.
Заключение
Архитектура — не искусство предсказывать будущее. Никто не знает, станет ли ваш продукт маркетплейсом, SaaS-платформой или нишевым инструментом для одной отрасли.
Архитектура — искусство уменьшать стоимость ошибок будущего:
- проектировать изменения, а не только функции;
- бороться со связанностью — главным убийцей систем;
- строить вокруг домена, а не технических слоёв;
- не делать систему универсальной — это самая дорогая ловушка;
- беречь данные — они переживут любой код;
- делить на независимые модули с явными контрактами;
- делать архитектуру понятной для людей, которых вы ещё не наняли;
- документировать решения — ADR, а не комментарии к каждой строке;
- следить за симптомами старения — и чинить границы, а не ждать «большой переписи».
Невозможно построить идеальную систему с первого раза. Но можно построить систему, где ошибки не накапливаются экспоненциально — где каждый спринт не удорожает следующий.
Именно это отличает долгоживущие системы от проектов, которые приходится переписывать каждые несколько лет — снова и снова, каждый раз обещая себе «на этот раз сделаем правильно».
Спросите себя не «как быстрее выпустить MVP», а «сколько будет стоить изменить это через три года». Ответ на этот вопрос — и есть архитектура на десятилетие.