ADR (Architecture Decision Records): как документировать архитектурные решения — шаблоны, примеры
Вы заходите в проект, открываете код и видите странное архитектурное решение. «Почему здесь gRPC, а не REST?», «Зачем мы храним состояния в Redis, а не в Postgres?», «Почему выбрали именно эту библиотеку, если она deprecated?». Автор решения давно ушёл, в тикетах — пустота, документация молчит. С этого момента начинается археология.
Architecture Decision Record (ADR) — это страховка от такой археологии. Короткий документ, который фиксирует: какое решение принято, в каком контексте и какие будут последствия. Не спецификация, не RFC, не дизайн-документ. Именно запись решения — почему мы здесь такие, а не иначе.
Что такое ADR и зачем они нужны
Формально ADR — это неизменяемая запись об архитектурном решении. Ключевых слова здесь три:
- Архитектурном — речь о структурных решениях, влияющих на систему в целом. Не о том, как назвать переменную, а о том, какой паттерн коммуникации выбрать между сервисами.
- Неизменяемая — принятый ADR не редактируется. Если решение изменилось — пишется новый ADR со статусом «Supersedes ADR-NNN». История остаётся.
- Запись — короткий, структурированный документ, а не стена текста. Обычно 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:
Почему не удалять устаревший 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 не должен быть дороже самого решения |
В сомнительных случаях применяйте правило трёх вопросов:
- Будет ли кто-то через год спрашивать «почему здесь так»?
- Есть ли реальная альтернатива этому решению?
- Повлияет ли решение на архитектурные характеристики системы?
Если на все три ответа «да» — пишите.
Как организовать 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
└── ...
Соглашения:
- Последовательная нумерация —
NNNN, начиная с0001. Не удаляйте номера устаревших ADR — они часть истории. - Формат имени:
NNNN-short-title-with-hyphens.md. Только латиница, только дефисы. - README.md — индекс: таблица всех ADR с номерами, названиями, статусами и датами. Первое, что читает новый разработчик.
- template.md: всегда актуальный шаблон (Nygard или MADR), который копируется при создании нового ADR.
- Один файл = один 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:
- Установите расширение «MADR» из маркетплейса
Ctrl+Shift+P→ «MADR: New ADR»- Заполните поля — расширение создаст файл и обновит 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:
- RFC создаётся и обсуждается (PR, Google Doc)
- Комментарии учитываются, консенсус достигнут
- RFC закрывается/вливается
- 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-практики:
- Один ADR — одно решение. Не смешивайте.
- ADR неизменяем после принятия. Хотите изменить — новый ADR со ссылкой на старый.
- Пишите контекст, а не только решение. Без контекста ADR бесполезен через год.
- Честно указывайте минусы. Идеальных решений не бывает — минусы предупреждают будущих разработчиков.
- Храните ADR в репозитории рядом с кодом. Документация, которая живёт отдельно, не живёт вообще.
- Связывайте ADR с требованиями. Каждый ADR должен отвечать на вопрос «какое требование породило это решение». Без трассировки решение выглядит произвольным.
Связанные статьи на сайте
- C4 Model: документируем архитектуру — визуализация архитектуры на 4 уровнях
- Справочник архитектурных паттернов — каталог проверенных решений
- Модульный монолит или микросервисы — когда что выбирать
- Архитектурные роли в ИТ — кто принимает решения
- Принципы архитектора ПО — 97 принципов из книги-сборника
- System Design: как проектировать масштабируемые системы — фреймворк проектирования и базовые компоненты
- Enterprise Architecture — архитектура предприятия и фреймворк TOGAF