Команда хранит данные в PostgreSQL, использует Redis как кеш и отправляет уведомления через очередь. Не случайный набор — за каждым выбором стоит история. PostgreSQL выбрали из-за транзакций. Redis запретили делать источником истины после потери данных. Очередь появилась, потому что API партнера падал и вебхуки терялись.

Через полгода приходит новый разработчик или AI-агент. Видит код. Причин не видит. Предлагает перенести состояние заказа в Redis, убрать «лишнюю» очередь, заменить PostgreSQL на что-то попроще. Каждое предложение выглядит логично в рамках одного файла. Каждое нарушает ограничения, которые команда уже оплатила своим опытом.

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

Что это

ADR (Architecture Decision Record, «запись об архитектурном решении») — короткий Markdown-файл, который фиксирует одно значимое архитектурное решение: какую проблему решали, что выбрали, какие альтернативы отклонили и какие последствия приняли. Не пересказ всей системы, не техническое задание. Одна запись — одно решение.

Идею предложил Майкл Найгард в статье «Documenting Architecture Decisions» в 2011 году. Суть проста: архитектурно значимые решения нужно хранить как небольшие неизменяемые записи рядом с кодом. Набор таких записей иногда называют Architecture Decision Log — журнал решений.

Вот как выглядит типичная папка ADR в репозитории:

docs/adr/
├── 0001-use-postgresql-as-source-of-truth.md
├── 0002-send-webhooks-through-outbox.md
├── 0003-isolate-ai-providers-behind-gateway.md
└── 0004-store-user-files-in-s3.md

Хорошую запись можно прочитать за две минуты. Ценность — не в объеме, а в сохраненном контексте.

Зачем нужно

Для разработки с ИИ это один из самых полезных видов документации. README объясняет, как запустить проект. Код показывает текущую реализацию. AGENTS.md задает правила работы агента. ADR объясняет, почему архитектурные границы выглядят именно так.

Ответ не только на «что мы выбрали», но и на вопросы поважнее:

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

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

Как устроено

Один файл — одно решение

ADR описывает решение, а не устройство всей системы. Типичный файл содержит:

  • Название решения.
  • Статус.
  • Контекст и проблему.
  • Принятое решение.
  • Последствия.
  • Рассмотренные альтернативы.
  • Условия пересмотра (опционально, но полезно).

Почему кода недостаточно

Допустим, в проекте есть интерфейс:

interface TextGenerator {
  generate(input: GenerateInput): Promise<GenerateResult>;
}

И две реализации:

class OpenAiTextGenerator implements TextGenerator {}
class LocalTextGenerator implements TextGenerator {}

По коду видно, что провайдеры изолированы общим контрактом. Но зачем это сделано — из кода не следует. Возможные причины: компания должна уметь отключить внешний API, часть данных нельзя отправлять в облако, стоимость моделей регулярно меняется, нужен fallback при сбое, разные клиенты используют разные модели, команда уже пережила сложную миграцию SDK.

Без этих причин AI-агент решит, что интерфейс избыточен, и заменит его прямым вызовом SDK. Тесты останутся зелёными, задача закроется, а архитектурная защита исчезнет.

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

Какие решения заслуживают ADR

Документировать каждую переменную не нужно. ADR полезен для решений, которые:

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

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

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

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

Статусы и жизненный цикл

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

СтатусЗначение
proposedРешение предложено и обсуждается
acceptedРешение принято и действует
deprecatedРешение больше не рекомендуется, но ещё встречается
supersededРешение заменено другим ADR

Некоторые команды добавляют rejected для важных отклонённых вариантов — если один и тот же спор регулярно возвращается.

Пример замены решения:

# ADR-0003: Изолировать AI-провайдеров за внутренним gateway

- Статус: заменено ADR-0014

Новый документ ссылается на старый:

# ADR-0014: Перенести маршрутизацию AI-запросов во внешний gateway

- Статус: принято
- Заменяет: ADR-0003

Не стоит переписывать старый ADR так, будто команда всегда знала правильный ответ. Ценность записи — в сохранении контекста на момент решения.

ADR, README, AGENTS.md и Memory Bank: что где хранить

Эти файлы решают разные задачи:

ДокументГлавный вопрос
README.mdКак понять, запустить и использовать проект?
AGENTS.mdКак AI-агент должен работать в этом репозитории?
Memory BankКаков текущий контекст, состояние и ближайшие цели?
ADRПочему принято конкретное архитектурное решение?
Task или issueЧто нужно изменить сейчас?

Пример распределения:

README.md:

Основная база данных проекта — PostgreSQL.

AGENTS.md:

Не добавляй другие постоянные хранилища без архитектурного согласования.
Перед изменением слоя данных прочитай docs/adr/0001-use-postgresql.md.

ADR:

PostgreSQL выбран источником истины из-за транзакций между заказом,
оплатой и журналом операций. Redis используется только как восстановимый кеш.

Memory Bank:

Сейчас выполняется перенос таблицы платежей на новую схему.
Этап dual write включен на dev.

Не нужно копировать один и тот же длинный текст во все документы. Коротких правил и ссылок на источник контекста достаточно.

Когда использовать

Как организовать ADR в репозитории

Для небольшого проекта достаточно простой структуры:

docs/
└── adr/
    ├── README.md
    ├── 0001-use-postgresql.md
    ├── 0002-use-s3-for-user-files.md
    └── 0003-isolate-ai-providers.md

В docs/adr/README.md удобно хранить индекс:

# Architecture Decision Records

| ADR | Решение | Статус |
|---|---|---|
| [0001](./0001-use-postgresql.md) | PostgreSQL как источник истины | принято |
| [0002](./0002-use-s3-for-user-files.md) | S3 для пользовательских файлов | принято |
| [0003](./0003-isolate-ai-providers.md) | Gateway для AI-провайдеров | принято |

Практические правила:

  • используйте последовательные номера;
  • храните один вопрос в одном файле;
  • пишите название как принятое действие, а не как общую тему;
  • добавляйте ADR в тот же pull request, где реализуется решение;
  • назначайте владельца или участников обсуждения;
  • не удаляйте заменённые записи;
  • связывайте новый ADR со старым;
  • держите документы рядом с кодом и проверяйте через обычное code review.

Название 0007-database.md слишком широкое. Название 0007-use-postgresql-as-order-source-of-truth.md сразу сообщает суть и границу решения.

Как подключить ADR к AI-агенту

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

## Архитектурные решения

- Перед изменением архитектуры, хранилищ, очередей, API-контрактов,
  аутентификации или AI-интеграций прочитай `docs/adr/README.md`.
- Действующие ADR имеют приоритет над предположениями из текущего кода.
- Не нарушай ADR со статусом `accepted` без явного предложения нового ADR.
- Для значимого решения сначала создай ADR со статусом `proposed`.
- Не переписывай историю: заменяй решение новым ADR со взаимными ссылками.

Полезно потребовать от агента назвать затронутые решения до редактирования:

Перед реализацией:

1. Найди ADR, связанные с задачей.
2. Кратко перечисли ограничения из них.
3. Сообщи, соответствует ли предложенный план действующим решениям.
4. Если возникает конфликт, останови изменение архитектуры и предложи новый ADR.

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

Как принимать ADR вместе с ИИ

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

Рабочий промпт:

Подготовь ADR со статусом proposed.

Проблема: нужно выбрать способ доставки событий о платежах во внешнюю CRM.

Сначала:
- собери ограничения из кода, документации и действующих ADR;
- перечисли decision drivers;
- предложи минимум три реалистичных варианта;
- для каждого оцени надёжность, сложность, стоимость, наблюдаемость,
  обратимость и влияние на текущую архитектуру;
- отдельно перечисли неизвестные факты, которые могут изменить выбор.

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

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

Пример

Готовый шаблон ADR

Универсального обязательного формата нет. Для большинства проектов достаточно такого шаблона:

# ADR-0007: Использовать PostgreSQL как источник истины для заказов

- Статус: принято
- Дата: 2026-07-11
- Авторы: команда backend

## Контекст

Какую проблему решаем? Какие ограничения, риски и требования важны?

## Решение

Что именно решили делать? Где проходит граница решения?

## Последствия

Какие преимущества получаем? Какую сложность и ограничения принимаем?

## Альтернативы

Какие варианты рассматривали и почему не выбрали?

## Условия пересмотра

При каких фактах или изменениях решение нужно открыть заново?

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

Например:

## Условия пересмотра

Пересмотреть решение, если:

- объём записей превысит 50 000 событий в секунду;
- появится требование автономной работы без центральной БД;
- стоимость эксплуатации станет выше согласованного бюджета;
- PostgreSQL перестанет выполнять требования по задержке после оптимизации.

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

Полный пример ADR для проекта с ИИ

Рассмотрим решение изолировать поставщиков моделей за внутренним интерфейсом.

# ADR-0003: Изолировать AI-провайдеров за внутренним gateway

- Статус: принято
- Дата: 2026-07-11

## Контекст

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

Нужно сохранить возможность:

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

## Решение

Бизнес-модули зависят от внутреннего интерфейса `TextGenerator`.
SDK провайдеров разрешены только внутри `infrastructure/ai/providers`.
Gateway нормализует ответы, ошибки и метрики, но не содержит бизнес-промпты.

## Последствия

Положительные:

- детали SDK не распространяются по проекту;
- провайдеры заменяются локально;
- тесты не требуют внешнего API;
- стоимость и ошибки наблюдаются в одном месте.

Отрицательные:

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

## Альтернативы

1. Прямые вызовы SDK в каждом сценарии: проще на старте, но усиливают связанность.
2. Внешний универсальный proxy: ускоряет интеграцию, но добавляет зависимость
   от отдельного сервиса и не решает правила маршрутизации домена.

## Условия пересмотра

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

Такой документ даёт AI-агенту важную информацию. Он может менять реализацию конкретного адаптера, но не должен переносить SDK в бизнес-модули без нового архитектурного решения.

Что писать в последствиях

Слабый ADR выглядит так:

## Последствия

Архитектура станет надёжнее и масштабируемее.

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

## Последствия

Положительные:

- HTTP-запрос больше не зависит от доступности CRM;
- неотправленные события сохраняются после перезапуска;
- повторная доставка контролируется idempotency key.

Отрицательные:

- появляется фоновый worker;
- доставка становится eventual consistent;
- нужны мониторинг очереди и процедура повторной обработки;
- порядок событий для одного клиента придётся обеспечивать отдельно.

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

Минимальный процесс без бюрократии

Для небольшой команды или личного проекта достаточно пяти шагов:

  • Заметить решение, которое дорого отменять или легко неправильно понять.
  • Создать короткий ADR со статусом proposed.
  • Сравнить альтернативы и явно записать компромиссы.
  • Принять ADR в том же pull request, где начинается реализация.
  • При изменении контекста создать новый ADR и пометить старый как superseded.

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

Ограничения

Ограничения

ADR не заменяет другие виды документации и не доказывает, что реализация соответствует решению.

Не заменяет тесты — ADR фиксирует решение, но не проверяет его исполнение.

Если в ADR написано «Redis только кеш», а новый модуль хранит там единственную копию заказа, документ сам по себе ничего не остановит.

Не заменяет схемы и API-спецификации — ADR объясняет причины, но не описывает текущее состояние системы.

Схемы данных и контракты живут отдельно.

Не заменяет runbook

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

Требует формализации для контроля — Полезно добавить architecture test, который запрещает, например, импорт SDK AI-провайдера вне каталога адаптеров.

ADR объясняет причину, тест обеспечивает границу.

Не заменяет обсуждение — ADR фиксирует результат обсуждения, но не само обсуждение.

Решение всё равно нужно проговаривать с владельцами системы.

Антипаттерны

Антипаттерны

Записывать после реализации — документ превращается в оправдание уже написанного кода.

Лучше создавать proposed ADR до крупного изменения и принимать его вместе с планом реализации.

Пропускать альтернативы — без альтернатив невозможно понять, был ли выбор осознанным.

Достаточно двух-трёх реалистичных вариантов с коротким объяснением.

Фиксировать только плюсы — у каждого архитектурного решения есть цена: задержка, сложность эксплуатации, зависимость, стоимость миграции.

Без минусов ADR — рекламный текст.

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

ADR должен быть маленьким и посвящённым одному решению.

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

Заменяйте, не удаляйте.

Превращать в закон навсегда — архитектурное решение действует в конкретном контексте.

Если контекст изменился, создайте новый ADR и замените старый.

Не указывать агенту маршрут — наличие папки docs/adr не гарантирует, что модель прочитает её перед локальной задачей.

Маршрут должен быть записан в AGENTS.md.

Чеклист

Чеклист

Заголовок

— принятое действие — название сообщает конкретное решение, а не общую тему.

Контекст описывает проблему

— читатель понимает, что болело, а не только что решили.

Требования отделены от предположений

— проверенные ограничения не смешаны с догадками.

Перечислены реальные альтернативы

— минимум 2–3 варианта с объяснением, почему не выбрали.

Записан критерий выбора

— понятно, почему выбран этот вариант, а не другой.

Записаны плюсы и минусы

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

Указана область действия

— где решение действует, где нет.

Есть условия пересмотра

— при каких фактах решение нужно открыть заново.

Нет конфликта с действующими ADR

— если есть — конфликт разрешён через superseded.

Реализация связана с решением

— можно проследить, что ADR исполняется в коде и тестах.

AI-агент знает, когда читать

— маршрут прописан в AGENTS.md или аналогичных инструкциях.

Ссылки

Ссылки