Architecture Decision Records
Фиксация архитектурных решений: зачем и как
Почему архитектурные решения исчезают
Вы приходите в новый проект, открываете кодовую базу и видите, что сервис авторизации написан на Go, хотя весь остальной стек — Java. Или что для очередей используется RabbitMQ, хотя у компании есть корпоративный стандарт на Kafka. Или что один из микросервисов общается с другим через gRPC, а все остальные — через REST. Вы спрашиваете у коллег «почему», и в лучшем случае получаете «так исторически сложилось», а в худшем — «Серёга знал, но он уволился в прошлом году».
Это стандартная ситуация, и проблема тут не в том, что решения были плохими — возможно, они были отличными для своего контекста. Проблема в том, что контекст испарился. Никто не записал, какие альтернативы рассматривали, какие ограничения были на тот момент, какие trade-offs приняли осознанно, а что получилось случайно. И теперь каждый новый разработчик либо боится трогать это решение (потому что «вдруг там есть причина»), либо бездумно переделывает его, наступая на те же грабли, от которых уже когда-то уклонились.
Формат Nygard
В ноябре 2011 года Майкл Найгард (автор книги Release It!) опубликовал короткую, на пару страниц, заметку «Documenting Architecture Decisions», в которой предложил формат, ставший стандартом де-факто. Идея Найгарда была в радикальной простоте: каждое значимое архитектурное решение фиксируется в отдельном коротком текстовом файле с четырьмя секциями.
Title — короткая именная фраза: «ADR-0005: Использование PostgreSQL вместо MongoDB для сервиса заказов».
Status — proposed, accepted, deprecated, superseded. Жизненный цикл решения: его предложили, приняли, а через два года заменили другим решением (и в старом ADR появляется ссылка на новый).
Context — описание ситуации, в которой принималось решение. Какие силы действовали: технические ограничения, требования бизнеса, сроки, экспертиза команды, существующая инфраструктура. Это самая ценная часть ADR, потому что именно контекст теряется первым.
Decision — что решили и почему. Не просто «выбрали PostgreSQL», а «выбрали PostgreSQL, потому что у нас уже есть DBA с экспертизой в PostgreSQL, данные имеют реляционную структуру, а MongoDB потребовал бы нанять нового специалиста и переписать существующий слой доступа к данным».
Consequences — что из этого следует: какие стали возможны, какие закрылись, что придётся сделать дополнительно. «Следствие: нам нужно мигрировать данные из прототипа, который был на MongoDB, это займёт спринт. Мы получаем транзакции и joins, но теряем гибкость схемы для быстрого прототипирования».
MADR и другие форматы
Оригинальный формат Найгарда был сознательно минималистичным, и со временем появились расширения. Самое популярное — MADR (Markdown Any Decision Records), которое добавляет секцию «Considered Options» с явным перечислением рассмотренных альтернатив и анализом trade-offs по каждой из них. MADR также предлагает указывать decision makers и статус подтверждения — полезно для организаций, где архитектурные решения проходят через формальное ревью.
Репозиторий Joel Parker Henderson на GitHub собрал десятки шаблонов ADR — от совсем минималистичных до развёрнутых с секциями для бизнес-обоснования, метрик успеха и плана отката. На практике стоит начинать с формата Найгарда и добавлять секции по мере необходимости, а не стартовать с тяжёлого шаблона, который отобьёт у команды желание писать ADR вообще.
Инструменты
adr-tools — набор bash-скриптов для создания и управления ADR. Команда adr new "Use PostgreSQL for orders" создаёт файл с правильным номером и шаблоном, adr list показывает все решения, adr link связывает ADR между собой. Простой инструмент, который делает одну вещь и делает её хорошо.
Log4brains — генерирует из ваших ADR красивый статический сайт с поиском и фильтрацией, что удобно для навигации по сотням решений в большом проекте.
VS Code расширение для ADR — автокомплит по шаблону и превью, хотя поддержка последних версий MADR не всегда актуальна.
Но по большому счёту ADR — это текстовые файлы, и вам не нужен специальный инструмент. Создайте папку docs/adr/ в репозитории, положите туда 0001-record-architecture-decisions.md (первый ADR обычно о том, что вы начинаете вести ADR), и начинайте. Формат NNNN-kebab-case-title.md — всё, что вам нужно для начала.
Когда это реально работает
ThoughtWorks Technology Radar поместили Lightweight Architecture Decision Records в категорию Adopt своего Technology Radar в ноябре 2017 года — это высшая категория рекомендации, означающая «мы считаем, что это нужно использовать в проектах по умолчанию». UK Government Digital Service (GDS) включили ADR в свои стандарты разработки как обязательную практику. Red Hat рекомендует ADR как инструмент для распределённых команд, где устные договорённости особенно быстро теряются.
ADR работают лучше всего, когда выполняются три условия. Во-первых, они лежат в том же репозитории, что и код — по принципу docs-as-code — не в отдельной вики, не в Notion, не в Google Docs. Во-вторых, решение о создании ADR встроено в процесс: если архитектурное изменение проходит через RFC или design review, ADR фиксирует итог этого обсуждения. В-третьих, ADR иммутабельны — вы не редактируете старый ADR, когда решение меняется, а создаёте новый со статусом «supersedes ADR-0005» и ссылкой на предыдущий. Так сохраняется история мышления, а не только текущее состояние.
Частые ошибки
Первая ошибка — писать ADR на каждое решение, включая «какую библиотеку использовать для логирования». ADR нужны для решений, которые сложно отменить: выбор базы данных, протокола коммуникации между сервисами, стратегии деплоя. Для мелких решений достаточно комментария в коде.
Вторая — слишком длинные ADR. Если ваш ADR занимает три страницы, его никто не прочитает. Формат Найгарда специально спроектирован так, чтобы один ADR помещался на один экран — это не случайность, а дизайн-решение.
Третья — не обновлять статусы. Если ADR-0003 принят два года назад, а вы с тех пор полностью ушли от этого решения, но в ADR статус всё ещё «accepted» — вы создали ту же проблему лживой документации, которую ADR призваны решать. Создайте новый ADR с новым решением и пометьте старый как superseded.