golang

ADR, архитектурные тесты и кейсы из прода: ресурсы, которые реально меняют код

  • четверг, 19 февраля 2026 г. в 00:00:15
https://habr.com/ru/articles/1001010/

У меня была привычка. Вижу классную статью про архитектуру — сохраняю. Репозиторий с примерами DDD — в закладки. Видео про CQRS — в плейлист «Посмотреть потом».

Вы знаете, чем кончаются плейлисты «Посмотреть потом».

В какой‑то момент закладок стало 300+. Половина ссылок битые, треть дублируют друг друга, остальное - статьи, которые казались гениальными в два часа ночи. Я сел и вычистил всё до 106 ресурсов. Собрал их в awesome-list на GitHub.

Но статья не про список. Статья про три вещи, которые я для себя открыл в процессе и которые почему-то мало обсуждают.

Что не попало и почему

Сначала про мусор, потому что это оказалось полезнее самого списка.

Мертвые репо. У меня висел репозиторий с примерами CQRS на PHP. 2000+ звезд, выглядит солидно. Вернулся через год — последний коммит 2020-го, три issue «does this work with PHP 8?» без ответа, все зависимости устарели на два мажора.

Клоны Refactoring.Guru. Их десятки. «Design patterns in Go», «Design patterns in Rust», «Design patterns in [подставь язык]». Открываешь — а там просто UML‑диаграммы из Refactoring.Guru переписанные в код. Без тестов, без контекста, без реальных примеров. Зачем, если оригинал и так покрывает 10+ языков и объясняет лучше?

Я оставил только те реализации, где есть что‑то сверху. DesignPatternsPHP — там PHP 8.1+, тесты, примеры из реальных проектов. faif/python‑patterns — идиоматичный Python, а не C++ переодетый в питон. Остальное — в корзину.

«10 ошибок архитектуры, которые совершает кажд»ый». Ну вы знаете этот жанр. Много слов, ноль конкретики. Мой фильтр: если из статьи нельзя вытащить что-то, что я применю в коде завтра - мимо.

ADR: практика, которую все игнорируют

Architecture Decision Records. Звучит бюрократично. Подождите уходить.

Ситуация. Приходите на проект, видите: авторизация через самописный middleware, хотя для фреймворка есть стандартный пакет. Новый человек в команде предлагает выкинуть это и поставить нормальную библиотеку. Логично же?

Проблема: middleware самописный, потому что нужна была кастомная логика мультитенантности, которую стандартная библиотека не умеет. Мы это знали. Но нигде не записали. Человек потратил два дня на исследование, прежде чем понял почему. Один файл на 15 строк сэкономил бы эти два дня.

ADR - это и есть этот файл. Формат простой: контекст (что происходит), решение (что выбрали), последствия (что из этого следует). Лежит в docs/adr/ рядом с кодом. Всё.

Придумал формат Майкл Найгард в 2011. Его главная мысль: когда новый человек видит странное решение, у него два варианта: слепо принять или слепо изменить. Оба плохие.

ADR дает третий: прочитать контекст.

Кто это реально использует

Я собрал 14 примеров. Вот те, которые стоит открыть:

Kubernetes - каждое изменение проходит через Enhancement Proposal. Хотите понять, зачем в k8s сделали Ephemeral Containers: открываете KEP, читаете контекст, альтернативы, trade-offs. Не гадаете и не гуглите.

Rust -каждая фича языка начинается с RFC. Async/await прошел через десятки итераций, прежде чем попал в язык. Вся история решений на виду.

Spotify - написали статью «когда писать ADR». Ответ: почти всегда. У них ADR отдельно помогают при передаче проекта между командами. Новая команда читает историю решений, а не гадает.

GOV.UK - британское правительство публикует свои архитектурные решения на GitHub (Если кажется, что ADR только для стартапов из Долины, вот вам госсектор)

Из тулзов: adr-tools (bash CLI), MADR (markdown-шаблон), log4brains (генерит статический сайт из ADR-файлов с поиском).

Если хотите попробовать - не выбирайте шаблон. Возьмите решение, которое приняли на прошлой неделе, и запишите в свободной форме. Контекст, решение, последствия. Закоммитьте. Всё, вы уже ведете ADR.

Архитектурные тесты: если правило не в CI, его нет

Знакомо: команда договорилась, что контроллеры не дергают репозитории напрямую, только через сервисы. Записали в Asana. Через месяц кто-то торопился перед релизом. На review не заметили. Через полгода 11 из 23 сервисов нарушают правило.

Я знаю, потому что это был мой проект. Я пошел считать, и мне стало грустно.

Решение - архитектурный тест. Пишете правило, CI его проверяет, билд падает если кто-то нарушил. Не документ, который можно проигнорировать, а красный билд, который не дает смержить.

Выглядит так:

Java (ArchUnit):

java@Test
void controllers_should_not_depend_on_repositories() {
    noClasses()
        .that().resideInAPackage("..controller..")
        .should().dependOnClassesThat()
            .resideInAPackage("..repository..");
}

PHP (Pest Arch):

phparch('controllers should not depend on repositories')
    ->expect('App\Http\Controllers')
    ->not->toUse('App\Repositories');

PHP (Arkitect):

phpRule::allClasses()
    ->that(new ResideInOneOfTheseNamespaces('App\Controller'))
    ->should(new NotDependsOnTheseNamespaces('App\Repository'))
    ->because('controllers should use services, not repositories directly');

Go (arch-go):

textdependsOn:
  - package: "**.controller.**"
    shouldNotDependsOn:
      - "**.repository.**"

Когда я добавил такой тест на свой проект, CI поймал три нарушения за первую неделю. Три PR, которые на review выглядели нормально, но тихо ломали архитектуру.

Полный набор по языкам:

Язык

Инструмент

Java

ArchUnit

C#

ArchUnitNET

PHP

Arkitect, Pest Arch

Go

arch-go

Kotlin

Konsist

Отдельная тема - Fitness Functions от ThoughtWorks. Идея шире: не только зависимости между пакетами, но и покрытие тестами не ниже порога, отсутствие PII в логах, error rate после деплоя меньше 1%. Архитектурные тесты - частный случай.

Shopify пришли к тому же с другой стороны. У них монолит на 2.8 млн строк Ruby, и они написали Packwerk - анализирует зависимости между компонентами и блокирует PR, которые нарушают границы.

Совет: не пытайтесь описать всю архитектуру тестами за один присест. Одно правило. Самое очевидное. «Домен не зависит от инфраструктуры». Добавьте, посмотрите как работает. Через месяц добавите второе.

Кейсы из прода > книги

Книга расскажет про паттерн. Кейс расскажет, когда паттерн не подошел и что делали вместо. Вот три, которые я рекомендую прочитать каждому.

Discord: триллион сообщений

Начинали с MongoDB, перешли на Cassandra, к 2022 выросли до 177 нод с триллионами сообщений. Cassandra не справлялась: garbage collection в JVM сжирала производительность, а hot partitions (популярные каналы, где тысячи людей одновременно) делали запросы непредсказуемо медленными.

Просто заменить базу - не вариант. Сначала написали промежуточный слой на Rust с request coalescing: когда тысячи пользователей одновременно запрашивают одно сообщение (анонс в большом канале), база получает один запрос, а не тысячу. И только потом мигрировали на ScyllaDB.

177 нод -> 72 ноды. Миграция триллионов сообщений за 9 дней. Ноль даунтайма.

Ценно тут не то, что они выбрали модную базу. А то, что проблему решила архитектура промежуточного слоя, а не замена хранилища.

Shopify: сознательный монолит

Все вокруг пилят микросервисы. Shopify - 2.8 млн строк Ruby, 500 000 коммитов - решил остаться на монолите и сделать его модульным.

Интересное из их статьи:

Модуляризация большого монолита - проблема людей, а не кода. Нельзя маленькой командой перепилить то, что активно развивают сотни разработчиков. Они начали разбивать монолит на компоненты (Rails Engines), но быстро поняли: если фокусироваться только на encapsulation и не чистить граф зависимостей, публичные интерфейсы превращаются в лишний слой indirection. Формально границы есть, реально всё зависит от всего.​

И ещё вывод, который мне засел в голову:

SOLID на уровне классов и «package principles» на уровне компонентов - одно и то же, только в разном масштабе. Если классы не следуют SOLID, компоненты с сильными границами не получатся.

Figma: real-time без лишней сложности

Первый дизайн-инструмент с настоящим collaborative editing. Могли взять Operational Transforms, как Google Docs. Не взяли - слишком сложно для их задачи.

Вместо этого - упрощенный подход на основе CRDT. Документ Figma - дерево объектов (как DOM). Конфликт возможен, только когда два клиента меняют одно свойство одного объекта. В этом случае побеждает тот, кто пришел на сервер последним.

Самая хитрая часть - одновременный reparenting. Клиент A перемещает X внутрь Y, клиент B перемещает Y внутрь X. Цикл. Сервер такое отклоняет.

Урок: не надо брать самый мощный инструмент. Figma сознательно выбрала подход проще OT. Правильный trade-off важнее правильного алгоритма.

Что ещё в списке

Всего 9 категорий, 106 ресурсов:

  • Reference Implementations - продакшен-примеры на Go, PHP, C# с DDD/CQRS/Event Sourcing, каталоги паттернов от AWS и Azure

  • Design Patterns - только то, что дает что-то сверх Refactoring.Guru

  • ADR - 14 примеров от Kubernetes до GOV.UK, инструменты, шаблоны

  • System Design - system-design-primer, ByteByteGo, Google API Design Guide, гайд по SQL-индексам

  • Design Documentation - C4 Model, Mermaid, D2, Diagrams as Code

  • Design Verification - всё из таблицы выше + фитнес-функции

  • Real-World Architecture - 10 кейсов: Discord, Shopify, Figma, Stripe, Netflix, Uber, Slack, GitHub, Cloudflare, Spotify

  • Books - 11 книг от Evans до Ousterhout

  • Community - Martin Fowler, InfoQ, Technology Radar, DDD Europe

Полный список

Если знаете ресурс, который реально поменял то как вы пишете код - кидайте в issues или PR. Особенно интересны примеры ADR из русскоязычных компаний и архитектурные тесты для языков, которых в списке пока нет.