lenec ru

← все посты

ADR на проекте: как фиксировать архитектурные решения, чтобы их потом читали

18K

За двенадцать лет я приходил в проекты, где «вся архитектура — в голове у Васи, который уволился полгода назад». Каждый раз одно и то же: команда боится трогать ключевые куски, потому что не понимает, почему они такие. Через год пишут с нуля. Через два — снова забывают, почему пришли к этому решению.

ADR (Architecture Decision Record) — это компромисс между «всё в Confluence» и «всё в голове». Маленький документ, который объясняет, почему было принято решение и какие альтернативы были отброшены. Звучит просто, но из тех команд, что я видел, рабочие ADR ведут максимум четверть. Остальные либо не пишут вообще, либо пишут так, что через полгода читать невозможно.

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

Зачем ADR, если есть Confluence

Главное отличие ADR от вики — иммутабельность. ADR пишется один раз и больше не правится. Если решение поменялось, появляется новый ADR, который ссылается на старый и помечает его как Superseded. Так получается срез истории: видно не только текущее состояние, но и путь, которым команда к нему пришла.

В Confluence страницы постоянно правятся. Через полгода ты открываешь «Архитектура платежей» и видишь актуальное описание, но не знаешь, почему именно так. Решение принимали в споре, в спор внесли пять трейдоффов, выбрали один — а на странице остался только результат. Через год кто-то предложит «сделать по-другому» и пойдёт по тем же граблям.

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

Минимальный шаблон, который работает

Я перепробовал несколько шаблонов: Майкл Найгард, Y-Statement, MADR. В итоге свёл всё к простой структуре, которая помещается в одну экранную страницу.

# ADR-0042: Использовать Outbox pattern для публикации событий

## Статус
Accepted, 2026-03-15

## Контекст
Сервис заказов публикует события OrderCreated в Kafka.
Коммит в Postgres и send в Kafka не атомарны: за месяц
собрали 12 кейсов, когда заказ есть в БД, но события нет.
Просадка по бизнес-метрикам около 0.3% — терпимо, но растёт
с ростом RPS.

## Решение
Вводим таблицу outbox в той же БД, что и заказы.
Запись в outbox делается в одной транзакции с заказом.
Отдельный воркер раз в 200 мс читает строки и публикует
в Kafka. Помечает sent_at после успешной отправки.

## Альтернативы
- Distributed transaction (XA): отвергли — Kafka не поддерживает.
- Только retry на producer: не решает проблему отката транзакции.
- Event sourcing: слишком большая перестройка для текущей зрелости.

## Последствия
Плюсы: атомарность, простой код, понятный воркер.
Минусы: задержка 200-500 мс между коммитом и событием.
Плата: дополнительная таблица, мониторинг отставания воркера,
очистка outbox по cron.

## Ссылки
- ADR-0019: переход на Kafka как основной брокер
- Issue #4521: расследование потерянных событий

Шесть секций. Больше не нужно. Меньше — недостаточно.

Что делает ADR читаемым

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

Контекст должен быть конкретным

Плохо: «Текущая архитектура не справляется с растущей нагрузкой». Это набор слов. Через год непонятно, что именно не справлялось.

Хорошо: «На пиках 8000 RPS PostgreSQL даёт p99 латентность 850 мс при целевых 200 мс. EXPLAIN показал, что 70% времени уходит на seq scan по таблице events объёмом 400 ГБ».

Цифры, имена таблиц, конкретные эндпоинты. Через два года читатель должен понять не только «что было плохо», но и «насколько плохо и где именно».

Альтернативы — самое важное

Половина ADR, которые я видел, эту секцию пропускают или пишут «рассмотрели другие варианты, отвергли». Это автоматически делает ADR бесполезным. Через год кто-то предложит ровно ту альтернативу, и команда снова пойдёт её изучать с нуля.

В этой секции пишите по 2-3 предложения на каждый отвергнутый вариант: что это, почему отвергли. Не надо защищать диссертацию, нужна короткая аргументация.

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

- **gRPC между сервисами**: отвергли — экосистема Java/Kotlin
  команды слабо знакома с protobuf, плюс нет поддержки в текущем
  API gateway.

- **GraphQL**: отвергли — ни один из 4 потребителей не нуждается
  в выборочной выдаче полей. Дополнительная сложность не окупается.

- **REST с JSON**: принято — это решение.

Последствия пишутся честно

В реальной архитектуре нет «выигрышных» решений. Всегда есть цена. Если в секции «Последствия» написано только «теперь будет быстро и масштабируемо» — это маркетинговый текст, а не ADR.

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

Через год честно описанные минусы помогают понять: либо мы научились с ними жить, либо они выросли в проблему — пора писать новый ADR.

Где хранить

Я держу ADR в репозитории в папке docs/adr/. Имена файлов — 0001-title-in-kebab-case.md. Нумерация сквозная по проекту, никогда не переиспользуется.

В корне репозитория — индекс docs/adr/README.md со списком всех ADR, статусами и краткими описаниями. Его генерирует простой скрипт по front matter:

#!/bin/bash
for file in docs/adr/[0-9]*.md; do
    num=$(basename "$file" | cut -d- -f1)
    title=$(head -1 "$file" | sed 's/^# //')
    status=$(grep -A1 "## Статус" "$file" | tail -1)
    echo "- [$num: $title]($file) — $status"
done > docs/adr/README.md

Запускается в pre-commit или в CI. Так индекс всегда актуален, и команда видит срез решений на главной странице репозитория.

Когда писать ADR

Не каждое решение — ADR. Если вы добавляете кеш в один сервис на одну ручку — это просто PR. Если вводите новое хранилище, новый протокол, новый паттерн взаимодействия между сервисами — это ADR.

Мой эвристический список:

  • Выбор между несколькими технологиями (язык, БД, брокер).
  • Архитектурный паттерн, который применяется больше чем в одном месте.
  • Решение по безопасности или compliance.
  • Любое решение, которое тяжело откатить.

Если решение легко откатить и оно затрагивает один кусок кода — ADR избыточен. Если откат стоит недели работы или влияет на интеграции — пишите.

Процесс согласования

В трёх компаниях я видел разные модели. Расскажу, какая у меня сработала.

ADR создаётся как PR со статусом Proposed. Автор пишет первый драфт за час, не выверяя каждое слово — главное, чтобы был контекст и предлагаемое решение. PR висит 3-5 рабочих дней. Архитекторы и тимлиды смежных команд оставляют комментарии.

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

После принятия PR мерджится со статусом Accepted и датой. Дальше документ не правится. Если решение проваливается — пишется новый ADR со статусом Supersedes ADR-0042. Старый помечается Superseded by ADR-0078.

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

За годы я насобирал коллекцию того, как ADR превращается в мусор.

ADR постфактум. Решение приняли полгода назад, теперь решили задокументировать. Получается реконструкция: автор пишет, как ему сегодня кажется удобным, забывая реальный контекст. Через год документ читается как фантастика. Лучше написать честный ADR с пометкой «зафиксировано задним числом, реальные альтернативы не помним».

Слишком много ADR. Команда из 5 человек написала 200 ADR за полгода — половина про то, какой линтер использовать. Это превращает архитектурную документацию в шум. ADR должен быть редким событием.

ADR без последствий. «Решили использовать X. Это будет лучше». Никакой конкретики, никакой цены. Через год читатель не понимает, почему «лучше» и в каком смысле.

Постоянное переписывание. Команда правит существующие ADR при изменении контекста. Это убивает главное свойство — иммутабельность. Если решение поменялось — новый ADR, не правка старого.

ADR в Notion/Confluence. Они там есть, но никто туда не ходит. Решения о коде живут вне репозитория, и про них забывают. Я пробовал и так и так — рядом с кодом всегда работает лучше.

Метрики, которые я смотрю

Через полгода после внедрения процесса я обычно проверяю несколько индикаторов.

  • Сколько ADR написано: меньше 5 за полгода — процесс не прижился. Больше 50 — порог сигнала слишком низкий.
  • Доля ADR со статусом Superseded: 10-20% — здорово, решения эволюционируют. 0% — либо команда боится менять, либо ADR пишут только под безопасные решения.
  • Цитируется ли ADR в обсуждениях PR. Если в код-ревью встречается «см. ADR-0042» — процесс живой. Если нет — документы пишут, но не читают.

Последний пункт — главный. ADR не самоценен. Он работает только если на него ссылаются при реальных решениях.

Что запомнить

ADR — это не документация, а память команды о трейдоффах. Шесть секций, иммутабельный документ, рядом с кодом, ссылки между версиями. Без секции альтернатив документ бесполезен — именно она объясняет, почему вы здесь, а не там.

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

Комментарии 1

  • Ирина Лисицына

    У нас в аналитике то же самое работает с определениями метрик. Раньше держала их в Notion-документе — никто не открывал, версии разъезжались. Перешли на формат, очень похожий на ADR: один файл на метрику в репо аналитики, статус (proposed/active/superseded), список stakeholder-ов, ссылка на предыдущую версию при изменении формулы. Главный плюс — изменение определения теперь идёт через PR, и нельзя тихо переписать MAU. Вопрос — как у вас на проекте проходит ревью таких ADR? Лиды команд только или ещё кто-то?

Войдите, чтобы оставить комментарий.