Заметки по архитектуре ПО, которые остаются полезными через месяцы
Заметки по архитектуре ПО работают лучше, когда фиксируют инварианты, запрещённые короткие пути и внешние зависимости, а не каждую деталь.

Почему большие документы перестают помогать
Большинство архитектурных документов начинаются с честного усилия. Потом выходят несколько релизов, сервис переименовывают, поставщик меняет условия, и одно срочное исправление нарушает план. Документ остаётся на месте, а система движется. После этого что‑то в нём верно, что‑то устарело, и никто уже не чувствует себя в безопасности, чтобы полностью ему верить.
Дрейф происходит быстро. У команд редко есть время перерисовать каждую диаграмму или переписать каждую справочную страницу после каждого изменения. Длинным документам нужен постоянный уход, а занятые команды обычно тратят это время на выпуск фич, исправление багов или поддержку клиентов.
Люди также перестают читать длинные страницы по простой причине. Они открывают документацию, когда им нужен ответ прямо сейчас. Если ответ спрятан под двенадцатью разделами, устаревшей диаграммой и стеной контекста, большинство читателей бегло просматривают страницу полминуты и сдаются. Тогда они спрашивают коллегу, догадываются по коду или копируют последний увиденный паттерн. Так старые ошибки возвращаются.
Полезная часть архитектурной документации меньше, чем думают многие команды. Не нужен идеальный зеркальный образ всей системы. Нужны те несколько решений, которые защищают систему, когда память тускнеет. Какие правила должны остаться верными после рефакторинга? Какие короткие пути недопустимы даже под дедлайном? Какие внешние системы могут вас сломать, если они изменятся?
Вот почему заметки по архитектуре работают лучше, когда они узкие. Короткая заметка может прожить месяцы, потому что люди откроют её перед рискованным изменением. Огромная страница обычно тихо умирает. Её никто не удаляет — просто перестают ей доверять.
Простая норма помогает. Если кто‑то может прочитать заметку за пять минут и избежать одной дорогой ошибки, заметка выполнила свою задачу. Если страница пытается описать всё, она обычно быстро устаревает. Если в ней записаны инварианты, запрещённые короткие пути и внешние зависимости, она остаётся полезной долго после релиза, который её породил.
Что должно быть в заметке
Полезная заметка сохраняет те части, которые остаются верными, даже когда тикеты, инструменты и размер команды меняются. Это не мини‑вики. Она содержит правила, которые важны через шесть месяцев.
Начните с инвариантов простым языком. Это факты, от которых зависит система, а не приятные идеи. Пишите строки вроде "все записи проходят через этот сервис", "задания могут выполняться дважды без вреда" или "данные клиента хранятся в одном регионе". Если кто‑то присоединился к команде завтра, он должен понять безопасные границы только по этим строкам.
Затем добавьте короткие пути, которые команда не позволит себе использовать. Это часто экономит больше времени, чем аккуратно нарисованная диаграмма. Под давлением люди обходят API, правят продакшн‑данные вручную, прячут секреты в исходниках или позволяют одному сервису лезть в базу данных другого. Если команда согласна, что такие ходы создают большие проблемы позже, запишите это ясно.
Во внешние системы тоже стоит включить. Назовите поставщиков, внутренние платформы и сторонние инструменты, которые могут разрушить план. Если продукт зависит от PostgreSQL, Cloudflare, GitLab CI или OpenAI pipeline, скажите об этом. Затем добавьте ту неисправность, которая важна: корзина работает при падении почты, деплоы останавливаются при падении CI, или лимиты по трафику отправляют запросы в очередь.
Пропускайте детали, которые меняются каждый спринт. Точные имена эндпоинтов, расположение классов, текущие размеры серверов и временные флаги быстро устаревают. Поместите их в код, тесты или runbook. Заметка должна хранить границы и предположения, а не каждую подвижную часть.
Короткая заметка часто помещается на один экран:
- правила, которые система должна сохранять
- короткие пути, которые команда отвергает
- внешние системы, которые могут отказать
- что команде нужно пересмотреть при изменении одного из этих пунктов
Этого достаточно. Через месяцы кто‑то должен открыть заметку и увидеть, что нельзя ломать, где сидит риск и какой быстрый фикс недопустим.
Начните с инвариантов
Рефакторы меняют имена файлов, границы сервисов и владение командами. Некоторые правила всё равно должны оставаться верными. Поместите эти правила в начало заметки — люди нуждаются в них, когда система становится грязной.
Инвариант — это правило, которое переживёт перезапись, миграции и срочные исправления. Если изменение его ломает, команда должна остановиться и спросить, изменился ли дизайн или изменение неверно. Это делает инварианты более полезными, чем длинное описание того, как код выглядит сейчас.
Держите каждый инвариант коротким. В большинстве случаев хватает одного предложения. Если кто‑то не может просканировать его за пять секунд во время инцидента, оно слишком длинное.
Хорошие инварианты обычно охватывают несколько простых областей:
- Данные: каждая запись клиента имеет один канонический дом, и импорты никогда не перезаписывают подтверждённые платёжные данные.
- Безопасность: только один сервис может выдавать токены авторизации, и каждое действие администратора оставляет запись аудита.
- Владение: одна команда утверждает изменения схемы для общей базы данных.
- Поведение при инциденте: система может замедлиться или перейти в режим только для чтения, но не должна терять принятые заказы или списывать деньги дважды.
Пишите правило и его предел. "Payment events are append-only" говорит больше, чем пара абзацев о биллинге. "User deletion stays soft-delete for 30 days" яснее, чем "пользователи могут удаляться по политике".
Это также помогает, когда люди improvisируют под давлением. Во время простоя инженеры часто применяют короткие пути, которые кажутся безобидными на десять минут и создают недели очистки позже. Короткий инвариант вроде "we never edit ledger rows by hand in production" предотвращает такие повреждения.
Если хочется объяснить всё, остановитесь и обрежьте. Заметки устаревают, когда пытаются отразить всю систему. Инварианты устаревают медленнее, потому что они описывают линии, через которые не должны переходить будущие изменения.
Простой тест: если новый инженер прочитает заметку, сможет ли он сказать, что должно оставаться верным после следующего большого рефактора? Если да, заметка поможет и через месяцы.
Запишите короткие пути, которые вы не допустите
Заметка становится полезнее, когда она называет плохие фиксы, к которым люди тянутся под давлением. Команды редко попадают в беду потому, что забыли большую диаграмму. Они попадают в беду, потому что кто‑то взял быстрый короткий путь, никто не записал это, и через полгода этот путь стал нормой.
Будьте откровенны. Если короткий путь запрещён, скажите это простыми словами и добавьте одно предложение почему. Новым участникам команды не должно приходиться догадываться, хитрый ли это обход или будущая проблема.
Для небольшой SaaS‑команды типичные проблемные места выглядят так:
- Никаких прямых правок базы данных в продакшене, если команда не написала миграцию или рецензируемый emergency‑скрипт. Ручные правки расходятся с логикой приложения и плохо отслеживаются.
- Никаких скрытых cron‑задач на случайных серверах. Если задача важна, команда кладёт её в репозиторий, называет владельца и логирует ошибки.
- Никакого обхода авторизации или лимитов ради "внутреннего использования". Такие исключения имеют свойство просачиваться в реальные пользовательские пути.
- Никаких ручных серверных изменений, которые живут только в истории shell. Если настройка важна, команда размещает её в коде или в письменном runbook.
Эти правила работают, потому что они останавливают повторяемый ущерб. Прямая правка базы может решить сегодняшний тикет и тихо сломать завтрашний деплой. Скрытая cron‑задача может поддерживать биллинг месяцами, а затем упасть при замене одного хоста. Ручная правка nginx может сэкономить десять минут и стоить дня во время восстановления инцидента, потому что никто не знает о ней.
Пишите каждое правило так, чтобы новый сотрудник мог действовать без дополнительных вопросов. "Избегать ручных изменений" слишком расплывчато. "Не изменяйте конфиг продакшена вручную. Обновите Terraform или скрипт деплоя вместо этого" — ясно.
Если хотите, чтобы заметки жили долго, включайте короткий путь, причину и безопасный путь. Это даёт команде стандартное действие под давлением, а именно тогда обычно формируются плохие привычки.
Отслеживайте внешние системы и поставщиков
Система редко ломается в изоляции. Она зависит от платёжных провайдеров, почтовых сервисов, DNS, облачного хранилища, очередей, карт, инструментов авторизации и API поставщиков. Если заметка их пропускает, люди тратят время на угадывание, где именно сидит риск.
Хорошие заметки называют каждую внешнюю зависимость, которая может остановить работу или сломать пользовательские функции. Это включает очевидных поставщиков вроде Stripe или AWS, но также менее заметные части, такие как очередь сообщений, хостинговый сервис резервного копирования базы, Cloudflare или LLM API, который используется в одном потоке.
Для каждой зависимости запишите четыре вещи: зачем вы её используете, кто отвечает за неё в команде, какие лимиты применяются и что происходит при её отказе. Владение важнее, чем команды обычно ожидают. Когда оповещения срабатывают в 2 часа ночи, кто‑то должен знать, звонить ли backend‑лиду, DevOps или основателю.
Короткий список часто работает лучше длинного абзаца:
- Отметьте ограничения по скорости, квоты и пороги выставления счёта.
- Запишите условия контракта, влияющие на архитектуру, такие как правила хранения данных, региональные ограничения или сроки уведомления о закрытии сервиса.
- Напишите первую точку отказа, а не каждую возможную. Таймауты, задержки вебхуков, истекшие токены и накопление очереди покрывают многое.
- Скажите, какие фичи деградируют, а какие полностью перестают работать.
- Добавьте владельца команды для каждой зависимости.
Это экономит реальное время во время инцидентов. Если доставка почты падает, пользователи могут не получать сброс пароля, но всё ещё пользоваться приложением. Если провайдер авторизации падает, никто не сможет войти. Если очередь стопорится, заказы могут накапливаться, хотя фронтенд всё ещё выглядит нормально. Это очень разные ситуации, и заметка должна делать их очевидными.
Одна мелкая привычка помогает: отделяйте "внешнюю зависимость" от "внутреннего сервиса, который ощущается как внешний". Самохостed GitLab runner или ваш собственный Redis‑очередь всё ещё могут быть зависимостью, которую стоит перечислить, потому что другие части системы зависят от них так же, как от поставщика.
Через месяцы этот раздел часто помогает больше, чем диаграммы. У поставщиков меняются цены, лимиты и аптайм. Ваша заметка должна прояснить радиус поражения до того, как изменение ударит по продакшену.
Создайте заметку за 20 минут
Хорошая заметка начинается с малого. Выберите одну систему или один рабочий процесс, к которому люди часто прикасаются, например логин, биллинг, деплой или импорт данных. Если вы попытаетесь описать всю компанию, вы застрянете, будете спорить о формате и ничего не сохраните.
Большинство полезных заметок помещается на один экран. Этого достаточно, если заметка захватывает несколько фактов, которые должны быть верными через шесть месяцев.
Первые 10 минут потратьте на запись трёх‑пяти инвариантов. Это правила, которые никто не должен нарушать, даже если они в спешке. Держите их простыми и проверяемыми. "Payments only go through one service" — полезно. "Платёжный слой должен оставаться чистым" — слишком расплывчато.
Потом потратьте около 5 минут на короткие пути, которые вы не допустите. Помогают прошлые инциденты. Может быть, кто‑то однажды правил продакшн‑данные вручную, пропустил очередь или хранил секреты в коде. Запишите это как запрещённые ходы. Это экономит больше времени, чем длинное описание идеального дизайна.
Завершите внешними зависимостями и владельцами. Ещё 5 минут будет достаточно.
- Перечислите системы, от которых вы зависите: платёжный провайдер, облачное хранилище, почтовый сервис или провайдер идентификации.
- Назовите владельца для каждой, даже если это конкретный человек, а не команда.
- Добавьте первую ожидаемую точку отказа: лимиты, истёкшие токены, плохие вебхуки или просто недоступность поставщика.
- Отметьте, где искать оповещения или логи, если нужно быстро проверить.
Сохраните заметку там, где команда уже работает. Если инженеры живут в репозитории, положите её рядом с кодом. Если команда работает в GitLab‑заявках или внутренней вики, храните её там. Идеальная заметка в неправильном месте легко забывается.
Если вы уже ведёте записи технических решений, заметка может лежать рядом с ними. Она выполняет другую задачу. Запись решения объясняет, почему вы что‑то выбрали. Эта заметка говорит следующему человеку, что нельзя ломать.
Простой пример от небольшой SaaS‑команды
Представьте пятеро человек в SaaS‑команде с одним веб‑приложением, месячной оплатой, входом по почте и регулярными письмами: квитанции, приглашения и сброс пароля. Им не нужна гигантская вики. Им нужна заметка, которая скажет следующему человеку, что должно оставаться верным при изменениях кода.
Короткая заметка для такой команды может выглядеть так:
Product: TeamFlow
Invariant
- A customer account can only read or change its own data.
- Every query that touches projects, invoices, or audit logs must include account_id from the authenticated session.
- Background jobs and admin tools follow the same rule. No bypass path.
Forbidden shortcut
- During urgent releases, do not use a support-only script to edit billing state directly in the database.
- Change billing state only through the billing service so webhooks, audit logs, and emails stay in sync.
External dependency
- Stripe is the source of truth for payment status.
- We store Stripe customer_id, subscription_id, last webhook event id, and local status.
- If Stripe and our database disagree, we trust the latest verified webhook and replay events before changing access.
Один такой инвариант делает много работы. Он говорит каждому новому разработчику, что границы клиента — это не рекомендация и не только проверка в контроллере. Правило применимо в запросах, задачах, скриптах и админских экранах. Если позже добавят задачу массового экспорта и забудут account_id, заметка даёт ревьюеру понятную причину заблокировать изменение.
Запрещённый короткий путь важен особенно тогда, когда команда устала и пытается выпустить ночью в пятницу. Прямые правки базы кажутся быстрее, но оставляют дыры. Клиент может получить доступ без квитанции или потерять доступ, хотя Stripe всё ещё показывает активную подписку. Запись одного запрещённого хода экономит часы очистки позже.
Раздел о зависимости помогает при онбординге, потому что отвечает на вопрос, который новички задают после первого багa в биллинге: "Какой системе мы доверяем?" Вместо того чтобы полдня копаться в коде, они за минуту видят правило, сохранённые поля и путь восстановления. Это и есть хорошая архитектурная документация: она сокращает неопределённость.
Ошибки, которые тратят время
Большинство заметок терпят неудачу по простой причине: они похожи на дневник. Команды пишут, что случилось в прошлом месяце, кто что сказал на совещании и почему выбор тогда казался верным. Через несколько месяцев никому не нужна история. Им нужно правило, которое всё ещё применимо сегодня.
Короткая заметка с текущими ограничениями лучше длинной хронологии. Если правило поменялось, обновите правило. Если старый выбор всё ещё важен, оставьте одну строчку о том, почему он актуален.
Ещё одна частая ошибка — копирование диаграмм в заметки и последующее их забвение. Устаревшая диаграмма вреднее, чем её отсутствие, потому что люди ей верят. Если очередь, граница сервиса или поток данных изменялись три раза, старая картинка поведёт людей не туда.
Диаграммы держите только если кто‑то будет обновлять их как часть обычной работы. Иначе лучше написать несколько фактов простым языком. Фраза "billing calls the payment provider directly" стареет лучше, чем картинка, которой никто не касался полгода.
Невнятные предупреждения тоже тратят время. "Не ломайте это" никому не помогает. Скажите, что именно должно оставаться верным. Например: "ID счёта должны оставаться стабильными после экспорта, потому что бухгалтерия импортирует их по ID." Это даёт разработчику конкретную проверку перед релизом.
Зависимости часто теряются в заметках совещаний, чатах или комментариях к тикету. Тогда лимит поставщика, cron‑задание или ручной шаг бухгалтерии становятся сюрпризом для следующего человека. Заметки должны ясно называть внешние системы, кто за них отвечает и что может пойти не так.
Длина сама по себе — проблема. Как только заметка становится настолько длинной, что приходится скроллить, просматривать и догадываться, люди перестают её использовать. Большинству команд лучше заметки, которые помещаются на один экран или близко к тому.
Хороший фильтр прост:
- Говорит ли заметка текущее правило?
- Видны ли внешние системы быстро?
- Поймёт ли новый инженер, что нельзя менять?
- Можно ли просканировать заметку за две минуты?
Если ответ «нет», режьте жёстче. Заметка, которую люди читают, лучше идеальной заметки, которую никто не открывает.
Быстрая проверка перед сохранением
Заметка заслуживает места, только если новый инженер может быстро её просканировать и безопасно внести изменение. Если для этого нужно двадцать минут, заметка уже слишком тяжёлая. Хорошие заметки достаточно короткие, чтобы прочесть их за один присест, и достаточно понятные, чтобы новичок повторил основные правила команде.
Прочитайте каждое правило и проверьте формулировку. Сильное правило говорит, что должно оставаться верным, даже если код, поставщик или команда изменятся. "All billing writes go through one service" — полезно. "Мы сейчас используем сервис X для биллинга" — просто снимок состояния.
Команды также должны называть короткие пути, к которым люди действительно тянутся под дедлайном. Говорите прямо. Если инженеры лазают в базу из случайных задач, напишите, что это запрещено. Если люди обходят очередь для срочной работы, тоже зафиксируйте это. Заметки живут дольше, когда говорят о реальных искушениях, а не о воображаемых лучших практиках.
Внешним системам нужен свой маленький блок в заметке. Назовите сервис, зачем он нужен, кто за него отвечает и что ломается при отказе. Это важнее длинной диаграммы в большинстве маленьких команд. Когда провайдер платежей, сервис авторизации или менеджер секретов падает, команде нужны быстрые ответы, а не урок истории.
Быстрый ревью обычно ловит слабые места:
- Кто‑то новый может прочитать заметку за ~пять минут и объяснить ограничения системы.
- Каждое правило описывает инвариант, а не предпочтение.
- Заметка называет реальные рискованные короткие пути команды.
- Внешние сервисы, поставщики и внутренние владельцы легко находятся.
- После следующего релиза один инженер сможет обновить заметку за пару минут.
Последняя проверка важнее, чем кажется. Если обновлять заметку неудобно, никто этого не будет делать. Держите формат простым, правила конкретными и исключите всё, что устаревает через месяц.
Что делать дальше
Выберите одну систему на этой неделе. Ту, которая будит людей, тормозит релизы или повторно вызывает одни и те же вопросы в чате. Напишите одну заметку для этой системы и остановитесь на этом. Одна полезная страница лучше папки с недописанной документацией.
Держите первую версию маленькой. Добавьте правила, которые должны оставаться верными, короткие пути, которые команда не примет, и внешние сервисы, которые могут испортить вам день. Если заметки уже разбросаны по тикетам, соберите лучшее в одном месте и позже удалите лишнее.
Простой первый проход помещается на одну страницу:
- что должно оставаться верным в продакшене
- чего инженерам нельзя делать, даже под давлением
- от каких поставщиков, API, очередей или источников данных зависит система
- какое недавнее решение всё ещё формирует дизайн
Затем используйте следующее реальное событие как тест. После следующего релиза, простоя или уродливого бага откройте заметку и исправьте то, чего не хватало. Команды обычно больше учатся на одном грубом разборе инцидента, чем на месяце аккуратного письма. Если заметка не помогла кому‑то ответить быстрее, обрежьте или перепишите её.
Когда одна заметка окажется полезной, сделайте это привычкой небольшой команды. Не нужен большой процесс. Просите короткое обновление, когда кто‑то меняет инвариант, добавляет зависимость или принимает решение, которое будет важно через шесть месяцев. Десять чистых минут после релиза могут сэкономить часы позже.
Полезно, когда один человек отвечает за формат. Ему не нужно писать каждую заметку. Он просто следит, чтобы заметки были короткими, читаемыми и достаточно единообразными, чтобы людям можно было доверять.
Если ваша команда хочет внешнего обзора, Oleg Sotnikov на oleg.is работает со стартапами и малыми компаниями как Fractional CTO и консультант. Такой обзор помогает, когда проблема не в нехватке документации, а в её избыточности и отсутствии ясных правил.
Часто задаваемые вопросы
Что такое архитектурная заметка, на самом деле?
Архитектурная заметка — это короткая страница, которая говорит команде, что должно оставаться верным, какие короткие пути запрещены и какие внешние системы могут нарушить поток. Она не пытается описать всю систему.
Насколько длинной должна быть архитектурная заметка?
Держите её достаточно короткой, чтобы прочитать примерно за пять минут. Если людям нужно пролистывать длинный исторический текст, они перестанут пользоваться заметкой, когда им нужен быстрый ответ.
Что считается инвариантом?
Пишите правила, которые должны пережить рефакторы и срочные исправления. Хорошие примеры звучат как all billing writes go through one service или accepted orders must never disappear, потому что ревьюер может проверить такие правила в реальных изменениях.
Стоит ли включать диаграммы?
Оставляйте диаграмму только если кто‑то будет обновлять её как часть обычной работы. Если команда не поддерживает диаграммы, лучше прописать правило простым языком — устаревшая картинка ведёт людей не туда.
Какие запрещённые короткие пути стоит записать?
Записывайте короткие пути, которые под давлением создают повторяющие проблемы. Редактирование базы данных вручную, скрытые cron‑задания, ручные изменения конфигурации в продакшене и обходы авторизации — типичные кандидаты, потому что к ним тянутся в спешке.
Как документировать внешние зависимости?
Перечислите каждого поставщика или внутреннюю платформу, которая может остановить работу или сломать пользовательские функции. Добавьте, зачем вы её используете, кто за неё отвечает, и что выходит из строя в первую очередь, чтобы команда знала, куда смотреть при инциденте.
Где хранить эти заметки?
Положите заметку туда, где инженеры уже работают. Для большинства команд это репозиторий, соседняя папка с документацией или то же место, где хранятся технические решения — люди доверяют заметкам, которые легко найти за секунды.
Когда нужно обновлять архитектурную заметку?
Обновляйте заметку, когда меняется инвариант, когда вы добавляете или удаляете зависимость, или когда инцидент выявил пропущенное правило. Не нужен большой ревью‑цикл — один инженер может коротко обновить заметку сразу после изменения.
Чем это отличается от ADR или runbook?
Запись решения объясняет, почему вы что‑то выбрали в определённый момент. Руководство по процедурам описывает, как выполнить задачу. Архитектурная заметка говорит следующему инженеру, что НЕЛЬЗЯ ломать.
У нас уже есть беспорядочные документы. С чего начать?
Начните с одного рабочего процесса, который причиняет боль: логин, биллинг, деплой или импорт данных. Напишите три‑пять инвариантов, одно‑два запрещённых коротких пути и внешние системы, затем отрежьте всё, что звучит как история.