Классическая сцена проектирования знакома многим командам: за столом собираются самые опытные инженеры, на доске растёт схема из квадратов и стрелок, и кто-то обязательно произносит фразу "надо заложиться на будущее". Через год выясняется, что будущее не совпало с прогнозом. Нагрузка выросла не в десять раз, а в полтора. Сценарий, ради которого городили сложность, вырезали из бэклога за одну встречу, а модуль "с запасом" тормозит каждое изменение. Проблема не в недостатке ума у проектировщиков, а в самом запросе: архитектура строится в условиях, где ключевых данных нет ни у кого. Рабочая альтернатива сортирует решения по стоимости отката и тратит глубокий анализ только там, где откат невозможен.

Почему попытка спроектировать архитектуру на пять лет вперёд проваливается

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

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

Кент Бек в книге об экстремальном программировании закрепил принцип YAGNI: не пиши того, что не понадобится, потому что "будущая потребность" почти всегда угадывается неверно, а код под неё пишется сегодня и стоит денег уже сейчас. Мартин Фаулер в заметке 2015 года о микросервисах описал закономерность, которую наблюдали почти все удачные истории: распределённые системы вырастали не из красивой схемы "на пять лет", а из монолита, который стал тесен. Сначала работающий монолит, потом границы. Не наоборот.

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

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

Двусторонние и односторонние двери в сортировке архитектурных решений

Джефф Безос в письме акционерам за 2015 год разделил решения на односторонние и двусторонние двери. Одностороннюю открываешь один раз, и пути назад нет: вернуться можно, но с убытками, болезненным пересмотром и потерянным временем. Двусторонняя дверь открывается свободно: вошёл, огляделся, вышел обратно. По его оценке, большинство ежедневных решений компании двусторонние, и потому их надо принимать быстро, располагая примерно 70 процентами нужной информации. Ожидание 90 процентов стоит дороже, чем ошибка с быстрым откатом. Для односторонних дверей правило обратное: медленно, методично, с полной картой последствий.

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

Критерий практичен и проверяется за минуту. Спросите: сколько людей и месяцев уйдёт на возврат к прежнему состоянию, если решение окажется ошибкой через полгода. Возврат за неделю силами одной команды - двусторонняя дверь: решайте за час и не собирайте комитет. Если возврат тянет за собой миграцию данных клиентов, координацию с внешними командами и выкуп обязательств перед партнёрами - дверь односторонняя, и она заслуживает отдельного анализа, сценария отказа и назначенного владельца.

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

Последний ответственный момент и удержание альтернатив

Принцип последнего ответственного момента пришёл из бережливой разработки, его описали Мэри и Том Поппендик в книге 2003 года. Решение откладывается до последнего момента, когда дальнейшая задержка сама становится решением и начинает стоить денег. Отличие от прокрастинации в том, что дата вычисляется заранее и проговаривается открыто. До этой даты команда спокойно собирает факты: замеры, эксперименты, данные мониторинга, обратную связь пользователей.

Второй инструмент - удержание альтернатив. В инженерной практике Toyota такой подход называют параллельными наборами: несколько конкурирующих вариантов прорабатываются одновременно до момента, когда неопределённость падает настолько, что выбор можно сделать без пересмотра. В разработке то же самое выглядит как два прототипа на одну неделю вместо одного варианта на две недели с риском переделки, как короткие технические эксперименты-шипы, как A/B-тесты на реальных пользователях.

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

Обратимость как инженерное свойство и приёмы её удешевления

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

{
  "checkout_v2": {
    "enabled": true,
    "rollout_percent": 5,
    "allowlist": ["qa", "internal"]
  }
}

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

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

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

Записи решений ADR как оперативная память команды

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

  1. заголовок и порядковый номер, например 0014;
  2. статус из набора "предложено", "принято", "заменено", "устарело";
  3. контекст решения с датой и известными на тот момент ограничениями;
  4. само решение одной формулировкой без компромиссных отступлений;
  5. последствия, включая то, что стало хуже.

Утилиты семейства adr-tools создают такую запись одной командой:

adr new "Хранить события в PostgreSQL вместо отдельного брокера"

Ценность записи не в бюрократии. Через год раздел контекста показывает аргументы, которые тогда казались разумными, и дату, когда они были разумны. Это лучший тренажёр против самоуверенности.

Кто владеет решением и когда обязательна эскалация

У каждого решения должен быть владелец. Фреймворк DACI распределяет роли: driver ведёт подготовку и собирает материалы, approver один и принимает решение, contributors дают экспертизу, informed узнают о результате. Односторонние решения требуют approver уровнем выше команды, двусторонние решаются внутри неё. Это не формальность, а защита от коллективной безответственности, когда решение "как бы все согласовали", а отвечать некому.

Провалы симметричны. Крупные организации перекладывают все решения в разряд односторонних: на каждое изменение собирается архитектурный комитет, три согласования и месяц ожидания. Безос в том же письме прямо называл эту болезнь: система тяжелеет и медлит там, где почти все двери двусторонние. Стартапы ошибаются в другую сторону: за час принимают решения, которые необратимы, - выкатывают в публичный контракт поле, которое придётся поддерживать вечно. Сортировка дверей лечит оба провала: убирает медлительность там, где можно быстро, и гасит скорость там, где нельзя.

Для долгоживущих технологических выборов полезен радар: инструменты раскладываются по кольцам "принимаем", "пробуем", "оцениваем", "держим на расстоянии", и набор пересматривается раз в квартал. Выбор PostgreSQL в 2022 и его проверка в 2025 это не одно решение на вечность, а цепочка повторных решений с новыми данными. Эскалация остаётся обязанностью каждого, кто заметил одностороннюю дверь в обычном тикете.

Признаки односторонней двери и честная цена отката

Публичный контракт, схема данных с записями пользователей, платёжные цепочки, форматы подписи и шифрования, форматы, заданные внешними стандартами и законодательством, первичное шардирование - список дверей, которые открываются один раз. Общий признак: после запуска у решения появляются внешние потребители, которых вы не контролируете и не переспросите. Ещё один маркер: односторонние решения прячутся внутри безобидных. Фраза "добавим поле в публичный JSON" звучит невинно, а через два года это поле читают сотни интеграций, и переименование стоит квартала переговоров.

Честный тест один: письменно опишите план отката. Если он умещается в абзац и не содержит слов "миграция данных клиентов" и "координация с партнёрами", дверь двусторонняя, и её можно открыть на 70 процентах информации. Если план занимает страницу и в нём есть двойная запись с перекрёстной проверкой - тратьте время на сбор 90 процентов информации, сценарии отказа и внешнюю экспертизу.

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

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