Кладовочка

О разном, чтобы было под рукой онлайн

ADR (Architecture Decision Records): как документировать архитектурные решения — шаблоны, примеры

Вы заходите в проект, открываете код и видите странное архитектурное решение. «Почему здесь gRPC, а не REST?», «Зачем мы храним состояния в Redis, а не в Postgres?», «Почему выбрали именно эту библиотеку, если она deprecated?». Автор решения давно ушёл, в тикетах — пустота, документация молчит. С этого момента начинается археология.

Architecture Decision Record (ADR) — это страховка от такой археологии. Короткий документ, который фиксирует: какое решение принято, в каком контексте и какие будут последствия. Не спецификация, не RFC, не дизайн-документ. Именно запись решения — почему мы здесь такие, а не иначе.


Что такое ADR и зачем они нужны

Формально ADR — это неизменяемая запись об архитектурном решении. Ключевых слова здесь три:

  1. Архитектурном — речь о структурных решениях, влияющих на систему в целом. Не о том, как назвать переменную, а о том, какой паттерн коммуникации выбрать между сервисами.
  2. Неизменяемая — принятый ADR не редактируется. Если решение изменилось — пишется новый ADR со статусом «Supersedes ADR-NNN». История остаётся.
  3. Запись — короткий, структурированный документ, а не стена текста. Обычно 1–3 страницы.

Какую проблему решает ADR: новый разработчик (или вы сами через год) понимает, почему система выглядит именно так. Не «почему бы нам не переписать это на микросервисы», а «вот контекст, в котором был выбран монолит, и вот trade-off, который мы осознанно приняли».

Главный принцип:

Одно решение — одна запись. Принял → неизменяемо.

ADR не заменяет дизайн-документы и RFC — он их дополняет. Дизайн-документ описывает «как», ADR отвечает на «почему». Это две разные оси документации, и они нужны обе.


Шаблон ADR: Nygard, MADR и Y-Statement — выбери свой

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

1. Классический шаблон Майкла Нигарда (Michael Nygard, 2011)

Самый простой и распространённый формат. Четыре секции, никаких излишеств. Именно с него началась вся история ADR — Майкл Нигард описал его в 2011 году в блоге, и с тех пор этот шаблон стал де-факто стандартом.

Структура:

  • Title — короткое название в виде «Решение X вместо Y»
  • Status — текущий статус (Proposed / Accepted / Deprecated / Superseded)
  • Context — описание ситуации, ограничений и проблемы
  • Decision — что решили и почему
  • Consequences — что из этого следует: плюсы, минусы, риски

Полный копируемый шаблон:

# ADR-NNN: [Короткое название решения]

## Status
Proposed | Accepted | Deprecated | Superseded by ADR-NNN

## Context
Опишите контекст: какие силы действуют (технические, бизнесовые, организационные),
какую проблему решаем, какие есть ограничения.
Будьте конкретны — цифры, даты, факты.

## Decision
Мы решили [конкретное действие], потому что [обоснование].
Укажите, какие альтернативы рассматривали и почему отклонили.

## Consequences
Что изменится после принятия решения.
* **Положительные:** что станет лучше, проще, быстрее
* **Отрицательные:** какие риски, долги, ограничения появились
* **Действия:** что нужно сделать для реализации решения

Пример заполнения (выбор базы данных):

# ADR-0003: Использовать PostgreSQL как основную СУБД

## Status
Accepted

## Context
Система должна поддерживать сложные аналитические запросы (JOIN по 5+ таблицам),
строгую согласованность данных (финансовые транзакции) и горизонтальное
масштабирование на чтение. Команда имеет опыт работы с PostgreSQL и MySQL.
Ожидаемый объём данных: 500 ГБ через 2 года, 10 000 транзакций/сек на пике.

## Decision
Мы выбрали PostgreSQL 16, потому что:
- Полноценная поддержка ACID и транзакционной целостности
- Богатая экосистема расширений (PostGIS, pgvector, TimescaleDB)
- Зрелые механизмы репликации (streaming replication, logical replication)
- Команда имеет 5+ лет опыта эксплуатации PostgreSQL в production

Отклонённые альтернативы:
- **MySQL 8.0** — слабее в аналитических запросах, нет CTE с рекурсией,
  хуже поддержка JSONB и полнотекстового поиска
- **MongoDB** — не подходит для транзакционных финансовых данных,
  сложнее обеспечить согласованность при JOIN-подобных операциях

## Consequences
* **Положительные:** единый стандарт СУБД, сильная согласованность,
  богатый инструментарий мониторинга и бэкапов
* **Отрицательные:** вертикальное масштабирование ограничено,
  потребуется внедрение пула соединений (PgBouncer)
* **Действия:** настроить streaming replication master-slave,
  внедрить PgBouncer, добавить мониторинг через pg_stat_statements

2. MADR (Markdown Architecture Decision Records)

MADR добавляет к шаблону Нигарда систематический анализ альтернатив: явный список рассматриваемых вариантов, драйверы решений и таблицу «за/против» для каждого варианта. Если Nygard — это «решение и контекст», то MADR — это «решение, контекст и почему не выбрали остальное».

Когда использовать MADR: сложные решения с 3+ осмысленными альтернативами, где trade-off неочевиден, и нужно оставить след рассуждений. Для простых решений MADR избыточен.

Полный копируемый шаблон:

---
status: proposed
date: YYYY-MM-DD
decision-makers: [Имя1, Имя2]
---

# [Короткое название решения]

## Context and Problem Statement
Опишите проблему: что происходит, почему нужно решение,
какие ограничения действуют. Будьте конкретны.

## Decision Drivers
* [Драйвер 1 — например, производительность, стоимость, сроки]
* [Драйвер 2]
* [Драйвер 3]

## Considered Options
* Option A — [название и краткое описание]
* Option B — [название и краткое описание]
* Option C — [название и краткое описание]

## Decision Outcome
Chosen option: "Option X", because [ключевое обоснование выбора].

### Consequences
* Good, because [положительное следствие 1]
* Good, because [положительное следствие 2]
* Bad, because [отрицательное следствие 1]
* Bad, because [отрицательное следствие 2]

## Pros and Cons of the Options
### Option A — [название]
* Good, because [аргумент за]
* Good, because [аргумент за]
* Bad, because [аргумент против]
* Bad, because [аргумент против]

### Option B — [название]
* Good, because [аргумент за]
* Bad, because [аргумент против]

### Option C — [название]
* Good, because [аргумент за]
* Bad, because [аргумент против]

3. Y-Statement (Олег Циммерман)

Максимально сжатый формат: одно предложение, пять слотов. Подходит для быстрой фиксации простых решений или как резюме внутри более крупного ADR. Автор — Олег Циммерман (Zimmerman), соавтор книги «Cloud Strategy».

Формат:

В контексте <use case / ситуация>,
сталкиваясь с <concern / проблема>,
мы выбрали <option / решение>,
чтобы достичь <quality / цель>,
принимая <downside / плата за решение>.

Примеры:

В контексте обработки платёжных транзакций,
сталкиваясь с требованием строгой согласованности данных,
мы выбрали реляционную БД (PostgreSQL) вместо NoSQL,
чтобы достичь ACID-гарантий и целостности финансовых данных,
принимая ограничения горизонтального масштабирования на запись.
В контексте микросервисной архитектуры с 12 сервисами,
сталкиваясь с проблемой отладки распределённых транзакций,
мы выбрали распределённую трассировку (OpenTelemetry + Jaeger),
чтобы достичь наблюдаемости сквозных запросов,
принимая накладные расходы 3–5% на сериализацию трейсов.

Y-Statement удобно вставлять в начало классического ADR как TL;DR — прочитал одно предложение, понял суть.

Какой шаблон выбрать?

Ситуация Шаблон
Простое решение с очевидным выбором Nygard
Сложное решение с 3+ альтернативами и неочевидным trade-off MADR
Нужно быстро зафиксировать — «лишь бы не забыть» Y-Statement
Стратегическое решение, которое будут перечитывать годами Nygard + Y-Statement в начале
Регулярная практика в команде (каждое решение — ADR) MADR как стандарт

Главное — единообразие внутри проекта. Не смешивайте форматы без причины. Выберите один шаблон и придерживайтесь его.


Статусы ADR: жизненный цикл решения

Каждый ADR проходит через состояния. Это не просто метка — статус определяет, можно ли на решение ссылаться и применять ли его.

Статус Значение Когда ставить
Proposed На рассмотрении. Решение предложено, но ещё не принято В момент создания ADR
Accepted Принято и действует. На решение можно ссылаться После согласования со стейкхолдерами
Rejected Рассмотрено, но осознанно отклонено Если в процессе обсуждения решили «нет»
Deprecated Больше не применяется, но не заменено явно Решение потеряло актуальность
Superseded by ADR-NNN Заменено новым решением. Ссылка на новый ADR обязательна Когда принято новое решение
Amended by ADR-NNN Частично изменено новым решением Когда изменена часть, а не весь ADR

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

Жизненный цикл ADR:

stateDiagram-v2 [*] --> Proposed : Создание ADR Proposed --> Accepted : Согласование Proposed --> Rejected : Отклонение Accepted --> Deprecated : Потеря актуальности Accepted --> Superseded : Новое решение Deprecated --> [*] Rejected --> [*] Superseded --> [*]

Почему не удалять устаревший ADR? Потому что контекст принятия решения — такая же ценная информация, как и само решение. Новый разработчик должен видеть эволюцию архитектуры: «Сначала мы выбрали MongoDB, но через год поняли, что аналитические запросы важнее, и переехали на PostgreSQL — вот ADR-0012, который это объясняет».


Пример ADR: реальные кейсы из open source

Теория — это хорошо, но как ADR выглядят в реальных проектах? Вот пять примеров из известных open-source репозиториев.

Пример 1: Home Assistant — консолидация Docker-образов

Контекст: Home Assistant поддерживал два типа Docker-образов: на основе Debian и на основе Alpine. Это удваивало работу по тестированию, CI/CD и поддержке.

Решение: Провести консолидацию и оставить только один базовый образ. После анализа выбрали Alpine — меньший размер (в 3–5 раз), быстрее сборка, меньше поверхность атаки.

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

  • Упростился CI/CD (один пайплайн вместо двух)
  • Потеря экосистемы Debian-пакетов (часть аддонов пришлось адаптировать)
  • Пользователи, полагавшиеся на Debian-образ, должны были мигрировать

Это отличный пример ADR, где trade-off очевиден и честно задокументирован: «Мы знаем, что часть пользователей пострадает, но выгода для проекта в целом перевешивает».

Пример 2: Home Assistant — пересмотр поддержки баз данных

Контекст: Home Assistant поддерживал SQLite (по умолчанию), MySQL, MariaDB и PostgreSQL — и все они вели себя по-разному. Каждый новый релиз ломал какую-то из БД, баг-репорты множились, а разработчики тратили до 30% времени на обход несовместимостей.

Решение: Ограничить официально поддерживаемый список до SQLite + MariaDB. Для PostgreSQL и MySQL — community-поддержка. Ввести жёсткие требования к минимальным версиям.

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

  • Радикально меньше багов, связанных с БД
  • Недовольство пользователей со старыми версиями MariaDB/MySQL
  • Чёткий сигнал: «Хотите другую БД — поддерживайте сами»

Пример 3: Обязательный первый ADR — Bootstrap ADR

Каждый проект, внедряющий ADR, должен начать с простого:

# ADR-0001: Record Architecture Decisions

## Status
Accepted

## Context
Мы начинаем новый проект. Архитектурные решения будут приниматься
регулярно, и мы хотим сохранять контекст каждого решения.

## Decision
Мы будем использовать Architecture Decision Records (ADR) для
документирования всех значимых архитектурных решений.
Формат: шаблон Майкла Нигарда.
Место хранения: docs/architecture/decisions/
Нумерация: последовательная, начиная с 0001.

## Consequences
* Все архитектурно-значимые решения должны быть задокументированы
  в виде ADR до начала реализации
* Каждый PR, затрагивающий архитектуру, должен ссылаться на ADR
* Новые члены команды используют ADR для погружения в архитектуру

Этот ADR делает саму практику ADR осознанным архитектурным решением. Без него ADR — просто папка с файлами, которую никто не читает.

Пример 4: Монорепо vs Мультирепо

Классическая дилемма, которая раскалывает команды. Вот как она задокументирована в коллекции Джоэла Паркера Хендерсона (joelparkerhenderson/architecture-decision-record):

Контекст: 5 команд разрабатывают 12 связанных сервисов. Код расходится, версии библиотек конфликтуют, CI/CD требует координации между репозиториями. При этом команды хотят независимости деплоя.

Рассмотренные варианты:

  • Монорепо с Nx/Turborepo
  • Мультирепо (отдельный репозиторий на сервис)
  • Гибрид: монорепо для общих библиотек, мультирепо для сервисов

Решение: Монорепо с Turborepo. Совместное владение кодом, атомарные кросс-сервисные изменения, единый линтинг и форматирование. Для независимости деплоя — теги и фильтры в CI/CD.

Последствия: Упростилась код-ревью кросс-сервисных изменений. Усложнилось управление доступом (нет возможности дать доступ только к одному сервису). Размер клона вырос до 2 ГБ, внедрили shallow clone.

Пример 5: Где хранить секреты

Контекст: Система использует десятки API-ключей, паролей БД, сертификатов. Часть нужна в production, часть — локально у разработчиков, часть — в CI/CD.

Решение:

  • Production: AWS Secrets Manager с автоматической ротацией
  • Разработка: 1Password CLI + .env.template (без реальных значений)
  • CI/CD: GitHub Secrets → инжект в переменные окружения на этапе билда

Последствия: Разделение зон ответственности (нет production-секретов на локальных машинах). Единая точка ротации в AWS. Дополнительная стоимость AWS Secrets Manager ($0.40/секрет/месяц).


Когда писать ADR, а когда не стоит

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

Если решение можно откатить за день — ADR не нужен. Если решение будет влиять на систему годами — ADR обязателен.

Пиши ADR когда:

Признак Почему
Влияет на архитектурные характеристики Производительность, безопасность, масштабируемость, надёжность — это необратимые изменения
Значительная стоимость или риск Если ошиблись, цена исправления высока
Трудно откатить Смена БД, коммуникационного протокола, языка программирования
Выбор между 2+ осмысленными альтернативами Есть реальный trade-off, а не «правильное vs неправильное»
Cross-team impact Решение затрагивает больше одной команды
Устанавливает прецедент Решение становится стандартом, на который будут ссылаться

НЕ пиши ADR когда:

Ситуация Почему не нужно
Тривиальное, легко обратимое решение Имя папки, правило линтинга, версия мелкой зависимости
Уже покрыто стандартами организации Нет нового решения — нет ADR
Эксперимент / PoC Результат эксперимента может стать ADR, но сам эксперимент — нет
Нет альтернатив Если путь один, это не «решение», а констатация факта
Написание ADR > реализация ADR не должен быть дороже самого решения

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

  1. Будет ли кто-то через год спрашивать «почему здесь так»?
  2. Есть ли реальная альтернатива этому решению?
  3. Повлияет ли решение на архитектурные характеристики системы?

Если на все три ответа «да» — пишите.


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

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

Рекомендуемая структура:

docs/architecture/decisions/
├── README.md              # Индекс всех ADR со статусами
├── template.md            # Шаблон для новых ADR
├── 0001-record-architecture-decisions.md
├── 0002-use-postgresql-as-primary-db.md
├── 0003-adopt-modular-monolith.md
├── 0004-use-kafka-for-event-streaming.md
├── 0005-use-jwt-for-service-auth.md
└── ...

Соглашения:

  1. Последовательная нумерация — NNNN, начиная с 0001. Не удаляйте номера устаревших ADR — они часть истории.
  2. Формат имени: NNNN-short-title-with-hyphens.md. Только латиница, только дефисы.
  3. README.md — индекс: таблица всех ADR с номерами, названиями, статусами и датами. Первое, что читает новый разработчик.
  4. template.md: всегда актуальный шаблон (Nygard или MADR), который копируется при создании нового ADR.
  5. Один файл = один ADR. Не объединяйте несколько решений.

Пример README.md:

# Architecture Decision Records

| Номер | Название | Статус | Дата |
|-------|----------|--------|------|
| 0001 | Record Architecture Decisions | Accepted | 2026-01-15 |
| 0002 | Use PostgreSQL as Primary DB | Accepted | 2026-02-01 |
| 0003 | Adopt Modular Monolith | Accepted | 2026-03-10 |
| 0004 | Use Kafka for Event Streaming | Superseded by 0007 | 2026-04-05 |
| 0005 | Use Redis for Session Storage | Deprecated | 2026-05-20 |

ADR журнал: как вести реестр решений

Реестр ADR — единая точка входа в историю архитектурных решений проекта. В большинстве команд его роль выполняет README.md в папке docs/decisions/: таблица, в которой каждая строка — одно решение.

Что должно быть в ADR-журнале:

Поле Зачем
Номер Ссылочный идентификатор (ADR-0001) для перекрёстных ссылок
Название Кратко: о чём решение
Статус Accepted / Deprecated / Superseded — действует ли решение сейчас
Дата Когда принято
Автор К кому обращаться за деталями

Правила ведения журнала:

  • Одна строка — один ADR. Новое решение добавляется в конец таблицы, старые не удаляются.
  • Статус обновляется при изменении. Решение заменили — поменяли статус в журнале и добавили ссылку на новый ADR.
  • Журнал — первое, что читает новичок. По нему за 10 минут видно эволюцию архитектуры: какие решения действуют, какие устарели и почему.
  • Автоматизируйте. Когда решений больше 20, adr generate toc или adr-log пересобирают журнал из файлов — не редактируйте таблицу вручную.

Журнал — это «лицо» вашей ADR-практики. Если он пуст или не обновляется, ADR живут «в стол», а не в процесс.


Инструменты для работы с ADR

Ручное копирование шаблонов и обновление README работает для 3–5 ADR. Когда их становится 20+, нужна автоматизация.

Инструмент Что делает Для кого
adr-tools (npryce) CLI: adr new "Title", adr supersede NNN, adr init — полный жизненный цикл из командной строки Команды, привыкшие к CLI
MADR Markdown-шаблон + VS Code расширение для создания ADR по форме Команды на VS Code
Structurizr Визуализация решений в связке с C4-диаграммами — видеть, какое решение на каком уровне архитектуры Архитекторы, работающие с C4
log4brains ADR как knowledge base: веб-интерфейс, поиск, граф связей, версионирование Проекты с 20+ ADR
Decision Guardian Показывает релевантные ADR прямо в PR — «это изменение конфликтует с ADR-0005» Команды, практикующие архитектурное ревью

Быстрый старт с adr-tools:

# Установка (macOS)
brew install adr-tools

# Инициализация в проекте
adr init docs/architecture/decisions

# Создание нового ADR
adr new "Use JWT for Service Authentication"

# Замена устаревшего ADR
adr new -s 0005 "Use mTLS instead of JWT"

# Генерация оглавления
adr generate toc > docs/architecture/decisions/README.md

Быстрый старт с MADR в VS Code:

  1. Установите расширение «MADR» из маркетплейса
  2. Ctrl+Shift+P → «MADR: New ADR»
  3. Заполните поля — расширение создаст файл и обновит README

ADR и архитектурные кванты

ADR хорошо сочетаются с концепцией Architectural Quantum (архитектурного кванта) — независимо развёртываемой единицы с высокой функциональной связностью. Вот как эти понятия работают вместе:

  • ADR определяет границы кванта: решение о том, какие компоненты входят в квант, а какие — за его пределами, само по себе заслуживает ADR.
  • Fitness functions проверяют соблюдение ADR: если ADR говорит «все коммуникации идут через Kafka», fitness-функция автоматически проверяет это в CI/CD.
  • ADR-лог показывает эволюцию архитектуры: последовательность ADR — это хроника того, как система росла, какие границы пересматривались и почему.

Связка ADR + архитектурные кванты + fitness functions — мощный фундамент для эволюционной архитектуры. ADR фиксирует «почему», кванты определяют «где границы», fitness-функции проверяют «соблюдается ли».


Даты, версионирование и жизненный цикл ADR

Зачем даты в ADR?

Главный вопрос, на который отвечает дата в ADR: когда было принято это решение? Без даты невозможно понять, было ли решение принято до или после ключевых событий в проекте — релиза, инцидента, смены технологического стека.

Типы дат в ADR

В зависимости от шаблона ADR может нести несколько разных меток времени:

Дата Назначение Когда фиксируется
Дата решения Когда команда согласовала решение В момент принятия
Дата создания Когда ADR впервые задокументирован При первом коммите
Дата принятия Когда статус сменился на Accepted После ревью
Дата последнего изменения Когда документ обновлялся При каждой правке
Дата устаревания Когда решение заменено новым При смене статуса
Review-by Плановая дата пересмотра При создании ADR

Как MADR обрабатывает даты

Шаблон MADR использует YAML-фронтматтер с единственным полем date:

---
date: 2026-07-18
status: accepted
decision-makers: [Иванов, Петров]
---

Важно: поле date в MADR — это дата последнего обновления решения, а не дата создания или принятия. Дату создания MADR рекомендует отслеживать через git log:

# Узнать, когда ADR был впервые добавлен:
git log --follow --diff-filter=A -- docs/decisions/0002-postgres.md

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

---
date: 2026-07-18        # дата последнего обновления
decided: 2026-07-15      # дата принятия решения
status: accepted
---

Нумерация и хронология

Номера ADR (0001, 0002, ...) сохраняют хронологический порядок даже при переписывании git-истории. Это проще, чем полагаться на даты:

docs/decisions/
├── 0001-record-architecture-decisions.md   # первый
├── 0002-use-postgresql.md                  # второй
├── 0003-adopt-modular-monolith.md          # третий
└── 0004-use-kafka-for-events.md            # четвёртый

ADR-таймлайн в README

Хорошая практика — поддерживать сводную таблицу в README.md, чтобы видеть хронологию всех решений:

| # | Название | Статус | Создан | Принят | Заменён |
|---|---------|--------|--------|--------|---------|
| 0001 | Record architecture decisions | Accepted | 2026-01-10 | 2026-01-10 | — |
| 0002 | Использовать PostgreSQL | Accepted | 2026-01-15 | 2026-01-17 | — |
| 0003 | Модульный монолит | Superseded | 2026-02-01 | 2026-02-03 | ADR-0007 |
| 0004 | Kafka для событий | Deprecated | 2026-02-10 | 2026-02-12 | ADR-0008 |

Решения с ограниченным сроком действия

Не все решения вечны. Бывает, команда сознательно принимает временное решение — «используем X, через 6 месяцев пересмотрим». Для таких случаев:

Вариант 1: добавить review-by в фронтматтер

---
date: 2026-07-15
review-by: 2027-01-15
status: accepted
---

Вариант 2: явно прописать в последствиях

## Consequences

Это решение временное: мы используем библиотеку X для очередей
6 месяцев (до 2027-01-15). Причина: команда не имеет опыта с Kafka,
а X даёт быстрый старт. Обязательный пересмотр в январе 2027 —
создать ADR, подтверждающий или заменяющий это решение.

Практический совет: заведите напоминание (GitHub Issue, Jira-тикет, событие в календаре) на дату пересмотра. ADR сам себя не пересмотрит.

Цепочки изменений: amended vs superseded

Когда решение эволюционирует, а не полностью заменяется:

ADR-0012: PostgreSQL как основная БД (Accepted, 2026-01-15)
  ├── ADR-0012-a: Добавить Redis для кеширования (Accepted, 2026-03-20)
  ├── ADR-0012-b: Перенести сессии в Redis (Accepted, 2026-05-10)
  └── ADR-0018: Мигрировать на PostgreSQL 16 (Supersedes ADR-0012-c, 2026-07-01)

Каждое дополнение (-a, -b) — это самостоятельный ADR со ссылкой на родительский. Полная замена — новый номер (ADR-0018) со статусом Supersedes ADR-0012-c.


Трассировка: связь ADR с требованиями

Почему это важно

Архитектурное решение не существует в вакууме. Каждый ADR должен отвечать на вопрос: какое требование породило это решение? Без этой связи ADR превращается в «мы так решили, потому что так модно» — и через год никто не вспомнит, зачем.

В терминах adr.github.io:

Architecturally Significant Requirement (ASR) — требование, которое оказывает измеримое влияние на архитектуру системы. Каждый ADR — это ответ на ASR.

Как ссылаться на требования в ADR

Способ 1: в контексте (Nygard)

## Context

Платёжный модуль должен обеспечивать идемпотентность согласно
требованию R-FIN-04. Текущий подход на MySQL привёл к дублирующимся
списаниям при сетевых сбоях (см. INC-2025-0047).

Способ 2: отдельная секция (расширенный MADR)

## Driving Requirements

| ID | Требование | Источник | Приоритет |
|----|-----------|----------|-----------|
| R-12 | Поддержка 10K одновременных пользователей | SRS §3.2 | P0 |
| US-89 | Администратор строит отчёты без влияния на OLTP | Jira PROJ-89 | P1 |

## Affected Requirements

- **Удовлетворено:** R-12 — пул соединений PostgreSQL обеспечивает целевую конкурентность
- **Создано:** R-41 — все сервисы должны реализовать health-check для мониторинга пула
- **Изменено:** R-15 — таймаут отчёта увеличен с 3 до 5 секунд
- **Аннулировано:** R-33 — требование к документному хранилищу теряет актуальность

Способ 3: ссылки на внешние системы

## Related Requirements

- Jira: [PROJ-789](https://jira.example.com/browse/PROJ-789)
- Confluence: [SRS v2.0](https://wiki.example.com/pages/SRS)
- GitHub: [Issue #1234](https://github.com/org/repo/issues/1234)

ADR как архитектурное требование

ADR не только отвечает на требования, но и создаёт новые. Когда команда решает «используем PostgreSQL», это становится архитектурным ограничением, эквивалентным требованию:

ADR → Требование: «Все новые сервисы взаимодействуют с данными через PostgreSQL. Введение альтернативной СУБД требует отдельного ADR.»

Такие производные требования проверяются fitness-функциями — автоматическими тестами, которые следят за соблюдением архитектурных границ. ArchUnit (Java), NetArchTest (.NET) или кастомные проверки в CI/CD:

// Пример fitness-функции: ADR-0012 запрещает другие БД
archRule()
    .classes().that().areAnnotatedWith(Repository.class)
    .should().dependOnClassesThat()
    .resideInAPackage("org.postgresql..")
    .check(classes);

Матрица трассировки: требования → ADR → код

Стандартная матрица RTM (Requirements Traceability Matrix) получает дополнительный слой:

Требование → ADR (решение) → Проектный артефакт → Код → Тест

R-FIN-04  →  ADR-0012 (PostgreSQL)  →  схема БД v3  →  schema.sql  →  migration.test.ts
          →  ADR-0015 (idempotency)   →  Payment API  →  payment.ts  →  idempotency.test.ts

ADR и RFC: в чём разница

Часто путают ADR и RFC (Request for Comments). На практике это два этапа одного процесса:

Аспект RFC ADR
Цель Предложить и обсудить изменение Зафиксировать принятое решение
Состояние Открыт для дискуссии Финальный (после принятия)
Содержание Полное предложение, альтернативы, открытые вопросы Решение, обоснование, последствия
Аудитория Команда (обсуждение) Будущие разработчики (понимание)
Когда До решения После решения

Типичный workflow:

  1. RFC создаётся и обсуждается (PR, Google Doc)
  2. Комментарии учитываются, консенсус достигнут
  3. RFC закрывается/вливается
  4. ADR фиксирует итоговое решение со ссылкой на RFC

В MADR для этого есть поле Technical Story или секция Links:

Technical Story: RFC-0012 "Переход на PostgreSQL" — https://github.com/org/repo/pull/456

ADR как код: Git-хранение и версионирование

Почему ADR должны жить в Git

ADR — это не просто документы, это часть кодовой базы. Хранение в Git даёт:

  • Атомарные изменения: решение, его обоснование и код, реализующий это решение, живут в одном репозитории и видны в одном PR
  • Полная история: git log показывает, когда и почему изменилось каждое решение
  • git blame на решения: в любой момент видно, кто предложил, кто одобрил
  • Code review архитектуры: архитектурные решения ревьюятся по тем же правилам, что и код

Хранение ADR в Confluence/Google Docs/Notion убивает связь между решением и кодом. Git восстанавливает её.

Pull Request = обсуждение архитектуры

Типичный рабочий процесс:

1. Разработчик создаёт ветку:    adr/0012-use-postgresql
2. Пишет ADR по шаблону MADR:    docs/decisions/0012-use-postgresql.md
3. Открывает Pull Request        ← обсуждение архитектуры ДО кода
4. Команда обсуждает в комментариях PR
5. Approval = решение принято, merge = ADR получает статус Accepted
6. Только ПОСЛЕ этого пишется код реализации

Это «сдвиг влево» архитектурного ревью: решения обсуждаются до того, как код написан, а не постфактум.

Что даёт PR-based подход:

  • Все обсуждения зафиксированы в истории PR — не теряются в Slack/чатах
  • Архитектор может быть назначен как обязательный ревьювер через CODEOWNERS:
    docs/decisions/    @architects-team
  • Комментарии к конкретным строкам ADR — как в коде

Git как журнал решений

# Полная история архитектурных решений
git log --oneline docs/decisions/

# Кто и когда изменил конкретное решение
git log -p docs/decisions/0005-use-postgresql.md

# Решения, принятые в определённый период
git log --since="2026-01-01" --until="2026-06-30" -- docs/decisions/

# Какие ADR изменились между релизами
git log v1.0..v2.0 -- docs/decisions/

git blame на архитектуру:

git blame docs/decisions/0005-use-postgresql.md

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

a1b2c3d4 (Алиса 2026-03-15 14:22) **Status**: Accepted
a1b2c3d4 (Алиса 2026-03-15 14:22) ## Decision
e5f6g7h8 (Боб   2026-03-18 10:05) - Добавлен connection pooling

Неизменяемость ADR в Git

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

Как выглядит diff при замене решения:

- **Status**: Accepted
+ **Status**: Superseded by [0012-postgresql](0012-postgresql.md)

Это гарантирует, что git log и git blame остаются надёжным источником истины. Не бывает ситуации «мы втихую переписали решение задним числом».

Автоматизация: CI/CD для ADR

ADR в Git позволяют автоматизировать проверки качества архитектурной документации:

Линтинг Markdown (markdownlint):

# .github/workflows/adr.yml
name: ADR Quality
on:
  pull_request:
    paths:
      - 'docs/decisions/**'

jobs:
  lint-adrs:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: DavidAnson/markdownlint-action@v2
        with:
          globs: 'docs/decisions/*.md'

Проверка ссылок (lychee):

lychee docs/decisions/*.md   # битые ссылки в ADR недопустимы

Валидация фронтматтера:

# Проверить, что у всех ADR заполнены status и date
for f in docs/decisions/*.md; do
  yq eval '.status, .date' "$f" || echo "INVALID: $f"
done

Проверка целостности ссылок между ADR:

# Superseded by должен указывать на существующий ADR
grep -r "Superseded by" docs/decisions/ | \
  while read line; do
    ref=$(echo "$line" | grep -oP '\d{4}-[a-z-]+')
    [ -f "docs/decisions/$ref.md" ] || echo "Dangling: $ref"
  done

Автогенерация README/index.md:

npx adr-log -d docs/decisions -i docs/decisions/README.md

Decision Guardian — GitHub Action, который при создании PR автоматически показывает релевантные ADR, если изменяются защищённые файлы:

- uses: DecispherHQ/decision-guardian@v1
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    decision_file: '.decispher/decisions.md'

Стратегии хранения ADR в репозиториях

Монорепо (стандарт):

project/
└── docs/decisions/
    ├── 0001-record-architecture-decisions.md
    ├── 0002-use-postgresql.md
    ├── 0003-modular-monolith.md
    ├── README.md
    └── template.md

Все решения в одном месте, рядом с кодом.

Мультипакетный монорепо (Log4brains):

project/
├── docs/decisions/              ← глобальные ADR
│   ├── 0001-use-kubernetes.md
│   └── 0002-use-grpc.md
└── packages/
    ├── backend/docs/adr/        ← специфичные для бэкенда
    │   └── 0001-use-fastapi.md
    └── frontend/docs/adr/
        └── 0001-use-svelte5.md

Отдельный ADR-репозиторий:

adr-repo/
└── decisions/
    ├── global/
    ├── service-a/
    └── service-b/

Подходит для организаций, где решения принимаются централизованно. Минус — потеря прямой связи с кодом.

Git submodules:

git submodule add https://github.com/org/adr-repo docs/decisions

Теоретически возможно, но на практике редко оправдано из-за сложности управления submodules.

Инструменты для ADR as Code

Инструмент Тип Что делает
adr-tools CLI (Bash) adr new, adr supersede, adr init — прямо в git-репозитории
Log4brains Node.js CLI + SSG log4brains adr new + статический сайт из git-истории
pyadr CLI (Python) pyadr propose/accept/reject/deprecate/supersede
dotnet-adr CLI (.NET) dotnet adr new, интеграция с .NET-проектами
adr-viewer Python Генерирует статический сайт из директории ADR
adr-log Node.js Автогенерация index.md со ссылками на все ADR
Decision Guardian GitHub Action Показывает релевантные ADR в PR
Backstage ADR Plugin Плагин Поиск ADR по всем репозиториям организации

Практический workflow: от нуля до ADR as Code

# 1. Инициализация
git init
mkdir -p docs/decisions
cd docs/decisions

# 2. Создание шаблона (MADR)
cp ~/templates/adr-template.md template.md

# 3. Первый ADR — решение использовать ADR
adr new "Record architecture decisions"
# → создаёт docs/decisions/0001-record-architecture-decisions.md

# 4. Подключение CI/CD
# → добавляем .github/workflows/adr.yml с линтингом

# 5. Рабочий цикл
git checkout -b adr/0002-use-postgresql
adr new "Use PostgreSQL as primary database"
# → заполняем шаблон
git add docs/decisions/
git commit -m "adr: use PostgreSQL as primary database"
git push origin adr/0002-use-postgresql
# → открываем PR, обсуждаем, принимаем, мёржим

# 6. История решений всегда доступна
git log --oneline docs/decisions/
# 7f3a2b1 adr: use Kafka for event streaming
# 4c5d6e7 adr: use PostgreSQL as primary database
# 1a2b3c4 adr: record architecture decisions

Резюме

ADR — это не бюрократия. Это страховка от археологии.

Три шаблона — Nygard, MADR, Y-Statement — покрывают весь спектр: от простого «зачем мы выбрали Postgres» до сложного многовариантного анализа. Выберите один и начните с ADR-0001: зафиксируйте само решение использовать ADR.

6 правил хорошей ADR-практики:

  1. Один ADR — одно решение. Не смешивайте.
  2. ADR неизменяем после принятия. Хотите изменить — новый ADR со ссылкой на старый.
  3. Пишите контекст, а не только решение. Без контекста ADR бесполезен через год.
  4. Честно указывайте минусы. Идеальных решений не бывает — минусы предупреждают будущих разработчиков.
  5. Храните ADR в репозитории рядом с кодом. Документация, которая живёт отдельно, не живёт вообще.
  6. Связывайте ADR с требованиями. Каждый ADR должен отвечать на вопрос «какое требование породило это решение». Без трассировки решение выглядит произвольным.

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