ADR
ADR: как фиксировать архитектурные решения и почему это важно
В любом проекте рано или поздно возникают вопросы: почему мы выбрали PostgreSQL, а не Cassandra? Почему решили делать микросервисы, а не монолит? Почему для обмена сообщениями используем Kafka, а не RabbitMQ? Если эти решения не задокументированы, через полгода в команде новый разработчик спросит: “А почему, собственно, так?” А ответа нет. Или хуже: кто-то решит “улучшить” систему, не зная, какие компромиссы были заложены.
ADR (Architecture Decision Record) — это документ, который фиксирует значимое архитектурное решение: его контекст, альтернативы, обоснование и последствия. ADR помогает сохранить знания, избежать “исторических ошибок” и упростить онбординг новых членов команды.
Что такое ADR и зачем он нужен
Architecture Decision Record (ADR) — это короткий текстовый документ (обычно от 0.5 до 2 страниц), описывающий одно архитектурное решение. Каждое решение фиксируется в отдельном файле. Со временем они собираются в реестр (ADR log), по которому можно проследить эволюцию архитектуры.
ADR отвечает на вопросы:
- В каком контексте принималось решение? (Какие были требования, ограничения?)
- В чем суть решения? (Что именно решили сделать?)
- Какие альтернативы рассматривались? (Почему не выбрали их?)
- Какие последствия (positive и negative) у решения?
- Кто принимал решение и когда?
Зачем нужны ADR:
- Сохранение контекста. Через год уже никто не помнит, почему выбрали одну БД вместо другой.
- Онбординг. Новый разработчик или аналитик может прочитать ADR и понять логику архитектуры.
- Предотвращение “велосипедов”. ADR — это напоминание, что альтернативы уже рассматривались.
- Дисциплина. Процесс написания ADR заставляет думать о компромиссах и последствиях.
- Рефлексия. При пересмотре архитектуры можно вернуться к старым ADR и оценить, правильными ли были решения.
graph LR
Decision[Архитектурное решение] --> ADR[ADR документ]
ADR --> Repository[Реестр ADR]
Repository --> Team[Команда]
Repository --> Future[Будущие члены команды]
Repository --> Audit[Аудит]
Формат ADR (стандарт Michael Nygard)
Классический формат ADR (Michael Nygard) состоит из следующих разделов:
- Title (заголовок). Краткое название решения, например, “Использовать PostgreSQL для хранения заказов”.
- Status (статус). Предложено, принято, устарело, заменено.
- Context (контекст). Описание ситуации, требований, ограничений, которые требуют принятия решения.
- Decision (решение). Какое решение принято (может быть список или просто текст).
- Alternatives (альтернативы). Какие другие варианты рассматривались? Почему они не подошли?
- Consequences (последствия). Положительные и отрицательные эффекты решения (что становится легче, а что сложнее).
Пример ADR (для системы заказов)
# ADR-001: Выбор базы данных для сервиса заказов
## Status
Принято (2025-01-15)
## Context
Сервис заказов должен хранить заказы и историю их изменений.
Требования:
- 10 000 заказов в секунду в пик (черная пятница).
- Строгая консистентность (нельзя потерять заказ и нельзя дублировать).
- Запросы: 90% по order_id (точечные чтения), 10% по user_id + status.
- Задержка p99 < 100 мс.
- Ожидается рост до 100 000 заказов/сек через 2 года.
Ограничения: команда имеет опыт работы с PostgreSQL и Cassandra.
## Decision
Использовать PostgreSQL в качестве основной базы данных для сервиса заказов.
Для масштабирования записи применить шардирование на уровне приложения (Citus).
Для чтения использовать реплики.
## Alternatives
1. **Cassandra.** Хорошо масштабируется, AP, но не поддерживает транзакции и сложные запросы.
- Отвергнута, так как нужна строгая консистенция и фильтрация по user_id + status.
2. **CockroachDB.** Распределенная SQL с ACID, но команда не имеет опыта, а лицензия дороже.
- Отвергнута из-за отсутствия экспертизы и стоимости.
3. **Шардирование PostgreSQL на уровне приложения (current choice).**
- Принято: знакомый стек, ACID, возможность шардирования через Citus.
## Consequences
### Положительные
- Строгая консистенция для заказов.
- Возможность использовать сложные запросы и JOIN.
- Знакомый стек (PostgreSQL + Citus).
### Отрицательные
- Шардирование добавляет сложность (перебалансировка, ключи шардирования).
- Запросы по user_id+status будут идти во все шарды (fan-out).
- Нагрузка на запись ограничена шардами (но 10k/сек достаточно).
- Нужно проектировать ключ шардирования (скорее всего, order_id, тогда запросы по user_id становятся дорогими).
## Related ADRs
- ADR-002: Выбор стратегии шардирования для PostgreSQL
- ADR-003: Репликация и отказоустойчивость сервиса заказовГде и как хранить ADR
Репозиторий кода (рядом с кодом). Лучшая практика — хранить ADR в папке docs/adr в том же репозитории, что и код (или в отдельном репозитории архитектурных решений). Преимущества:
- ADR версионируется вместе с кодом.
- При работе над фичей разработчик видит связанные ADR.
- Можно ссылаться в pull request’ах.
wiki/Confluence. Тоже вариант, но у ADR должна быть история изменений. В wiki это сложнее, чем в git.
Именование: NNN-title-with-dashes.md, где NNN — порядковый номер (ADR-001, ADR-002…). Нумерация глобальная.
Реестр (log). Создайте файл README.md в папке adr, где перечислены все ADR с номерами и заголовками.
Жизненный цикл ADR
graph LR
Proposal[Предложено] --> Accepted[Принято]
Accepted --> Deprecated[Устарело]
Deprecated --> Superseded[Заменено]
Accepted --> Superseded
- Proposed (предложено). Решение предложено, но еще не утверждено. Обсуждается в команде.
- Accepted (принято). Решение утверждено (архитектором, командой). Действует.
- Deprecated (устарело). Решение больше не рекомендуется, но пока не заменено.
- Superseded (заменено). Решение заменено другим ADR. В поле “Superseded by” указывается номер нового ADR.
При изменении архитектуры не редактируйте старый ADR (он отражает историю), а создайте новый со статусом “Superseded”, указав, какой ADR он заменяет.
Когда стоит писать ADR
Не каждая мелочь требует ADR. Пишите ADR для решений, которые:
- Имеют долгосрочные последствия (выбор БД, архитектурного стиля, паттерна).
- Обсуждались командой (был спор, несколько альтернатив).
- Могут быть пересмотрены позже.
- Влияют на несколько компонентов системы.
- Нетривиальны (не “используем PostgreSQL для простого CRUD”).
Примеры без ADR: “Добавили кэш для каталога на Redis”. Это тривиально. Примеры с ADR: “Выбрали Redis, а не Memcached, из-за поддержки структур данных (Redis Sets для индексации недавних заказов).”
Как аналитик может участвовать в ADR
Системный аналитик — ключевой стейкхолдер при принятии архитектурных решений. Вы:
- Поставляете требования (функциональные и не функциональные). Без них ADR бессмыслен.
- Задаете контекст: какие процессы, какие ограничения, кого затронет решение.
- Участвуете в обсуждении альтернатив: понимаете бизнес-последствия решения.
- Документируете ADR (или проверяете качество документации). Часто ADR пишут разработчики, но аналитик должен проверить, учтены ли все требования.
- Следите за обратной совместимостью (API, данные). При изменении архитектуры аналитик обновляет контракты.
Типичные ошибки при написании ADR
- Слишком детализировано (down to implementation). ADR про выбор PostgreSQL с Citus, а не про параметры
wal_buffers. Оставьте детали для инженеров. - Отсутствие альтернатив и обоснования. “Выбрали PostgreSQL, потому что он крутой”. Это не ADR.
- Отсутствие негативных последствий. Архитектура — это всегда компромисс. Обязательно напишите, от чего пришлось отказаться.
- ADR пишут после реализации. ADR должен быть написан до или в момент принятия решения. Постфактум люди забывают альтернативы и риски.
- ADR живут отдельно от кода. В корпоративной wiki ADR никто не читает. Храните их в репозитории.
Примеры плохого и хорошего ADR
Плохой ADR (бесполезен):
Title: Использовать PostgreSQL.
Status: Accepted
Context: Нужна база данных.
Decision: Используем PostgreSQL.
Alternatives: Нет.
Consequences: Нет.Хороший ADR:
Title: Использовать PostgreSQL с шардированием (Citus) для сервиса заказов
Status: Accepted
Context: Сервис заказов: 10k записей/сек, строгая консистенция, 90% запросов по order_id, ожидаемый рост 100k/сек.
Decision: PostgreSQL + Citus. Шардирование по order_id (хэш). 16 шардов.
Alternatives:
- Cassandra: AP, сложные запросы по user_id+status неэффективны.
- CockroachDB: хороша, но нет опыта в команде.
Consequences:
+ ACID, знакомая команде БД.
+ Возможность сложных запросов.
- Запросы по user_id идут во все шарды (fan-out).
- Ключ шардирования (order_id) нельзя менять после создания.ADR и архитектурная документация
ADR — это не замена всей архитектурной документации, а дополнение. ADR фиксируют решения. Вся архитектурная документация (диаграммы C4, контекст, компоненты) описывает состояние системы. Они не заменяют, а дополняют друг друга.
- Архитектурная документация: как система устроена сейчас (статическая картинка).
- ADR: почему система устроена именно так (динамика, история, компромиссы).
Резюме
ADR — это инструмент для фиксации архитектурных решений. Он помогает сохранить контекст, обосновать выборы и избежать будущих споров.
Основные элементы ADR:
- Title (заголовок)
- Status (статус)
- Context (контекст, вызовы)
- Decision (решение)
- Alternatives (альтернативы)
- Consequences (последствия)
Где хранить: в репозитории в папке docs/adr с именованием NNN-title.md.
Когда писать: для значимых, спорных, имеющих долгосрочные последствия решений.
Ошибки: нет альтернатив, нет негативных последствий, нет контекста.
Для аналитика: ADR — это мост между бизнес-требованиями и технической реализацией. Вы отвечаете за качество контекста (требования) и за то, чтобы решение не противоречило нефункциональным ожиданиям (масштабируемость, безопасность). Участвуйте в создании ADR, а не только читайте их. Это поднимет уровень всей команды.