Инженерная практика: анатомия системы и карта трека
Есть два способа узнать, как устроена разработка. Первый — выучить по отдельности браузер, базу данных, Docker, Git и десяток фреймворков. Второй — понять, как из этого собирается одна работающая система, и уже потом углубляться туда, где болит. Первый способ даёт человека, который знает много слов и не может довести фичу до продакшена. Второй даёт инженера.
Этот трек — про второй способ. Он не заменяет специализированные треки портала: по базам данных
на портале двадцать глав, по фронтенду восемнадцать, по DevOps восемнадцать, по Git четырнадцать.
Он делает то, чего не делает ни один из них: показывает стыки. Как клиент договаривается
с сервером. Почему решение о хранилище тянет за собой решение о масштабировании. Почему
«нам нужны микросервисы» — это чаще всего вопрос про организацию команды, а не про технологию.
Что происходит с кодом между моментом, когда вы нажали git push, и моментом, когда его увидел
пользователь.
Дальше — сквозной разбор анатомии системы, карта ответственности между треками портала и описание того, как читать этот трек.
1. Один клик: сквозной путь через всю систему
Начнём с конкретного. Пользователь нажимает «Оплатить» в интернет-магазине. Между нажатием и надписью «Заказ принят» проходит 400 миллисекунд, и за это время изменение пробегает через все слои, которым посвящены главы этого трека.
(браузер / приложение) participant E as Периметр
(CDN, балансировщик) participant A as Прикладной сервис participant D as Хранилище participant Q as Очередь participant W as Фоновый воркер U->>C: клик «Оплатить» C->>C: локальная валидация формы
(быстрый отказ, ради UX) C->>E: HTTPS POST /orders
+ идемпотентный ключ E->>E: TLS, лимит скорости, выбор экземпляра E->>A: проксирование запроса A->>A: аутентификация, авторизация,
серверная валидация (ради корректности) A->>D: BEGIN; проверка остатка; INSERT order D-->>A: OK A->>Q: публикация события OrderCreated A->>D: COMMIT A-->>E: 201 Created + Location E-->>C: ответ C-->>U: «Заказ принят» Q->>W: доставка события W->>W: резервирование оплаты, письмо, аналитика
В этой картинке спрятано почти всё содержание трека.
- Клиент валидирует форму, но его валидации никто не верит: он работает на чужом устройстве, в чужой сети, и его код можно переписать в консоли браузера. Об этом — «Клиентская сторона».
- Сервер принимает запрос, проходит по цепочке промежуточных обработчиков и решает, что считать истиной. Об устройстве этого пути, моделях конкурентности и таймаутах — «Серверная сторона».
- Хранилище обеспечивает атомарность: либо заказ создан и остаток списан, либо ничего. О том, как выбирать хранилище и как не выстрелить себе в ногу ORM-ом, — «Слой данных».
- Контракт между клиентом и сервером — идемпотентный ключ, код ответа, формат ошибки, правила ретрая. Об этом — «Интеграция».
- Форма системы — то, что здесь показано как «сервис + очередь + воркер», могло быть одним процессом или пятнадцатью. Об осознанном выборе формы — «Архитектурные шаблоны на практике».
Всё это — только правая половина картины: то, как система работает. Есть вторая половина — то, как она появляется и меняется: контроль версий, окружение, сборка, ворота качества, конвейер, конфигурация. Ей посвящены главы 06–11.
2. Три контура обратной связи
Инженерная практика удобно раскладывается на три контура, различающихся временем отклика. Это не абстракция: почти каждая практика, о которой мы говорим дальше, существует ради того, чтобы сократить время какого-то из этих контуров.
Инженер отличается от «человека, который пишет код» тем, что видит все три контура сразу и понимает цену задержки в каждом. Тест, который идёт сорок минут, ломает первый контур. Ручной выкат по инструкции в вики ломает второй. Отсутствие внятных логов ломает третий — и тогда починка одного бага занимает не полчаса, а два дня.
Практическое следствие, к которому мы будем возвращаться: любое инженерное улучшение можно оценить вопросом «на сколько это сокращает петлю обратной связи и за какие деньги». Это работает и для линтера, и для микросервисов. Метрики DORA — по сути измерение второго и третьего контуров; о них подробно в «CI/CD» и в треке Project Management.
3. Анатомия: кто за что отвечает
Слово «слой» перегружено, поэтому договоримся о значениях сразу — эта путаница стоит командам недель споров.
| Термин | Что это | Пример |
|---|---|---|
| Слой (layer) | логическое разделение кода по ответственности | представление / прикладная логика / доступ к данным |
| Ярус (tier) | физическое разделение по узлам развёртывания | браузер / сервер приложений / сервер БД |
| Сервис | единица независимого развёртывания | orders, billing, search |
| Модуль | единица логической связности внутри кодовой базы | пакет, namespace, bounded context |
Слои и ярусы независимы: трёхслойное приложение может жить в одном процессе на одном ярусе. Именно смешение этих понятий рождает фразы вроде «у нас трёхуровневая архитектура, значит, три сервера». Подробный разбор — в главе 05.
Общая карта ответственности:
система)) Клиент UI и взаимодействие локальное состояние офлайн и кэш недоверенная среда Сервер маршрутизация прикладная логика авторизация фоновая работа Данные модель и схема транзакции миграции резервные копии Интеграция контракт API события и очереди надёжность вызова эволюция контракта Поставка контроль версий сборка и артефакты ворота качества конвейер Эксплуатация конфигурация наблюдаемость инциденты техдолг
4. Где на портале лежит глубина
Это, пожалуй, самая полезная таблица трека. Если вам нужно разобраться в теме до дна, идти надо не сюда, а в специализированный трек. Этот трек объясняет, зачем тема нужна и как она стыкуется с остальными; специализированный — как она устроена внутри.
| Тема | Что даёт этот трек | Куда идти за глубиной |
|---|---|---|
| Браузер, HTML/CSS/JS, React | роль клиента, границы ответственности | frontend |
| Мобильные приложения | клиент как класс задач | mobile |
| Сети, HTTP, TLS, DNS | путь запроса на уровне приложения | networking |
| Базы данных | выбор хранилища, ORM, миграции | databases |
| Стили API | контракт и его эволюция | architecture-patterns / API |
| Архитектурные шаблоны | карта форм и процедура выбора | architecture-patterns |
| Паттерны проектирования | отличие от архитектурных | design-patterns |
| Моделирование предметной области | границы модулей | DDD |
| Git | зачем VCS и как встроен в процесс | git |
| Редакторы и IDE | критерии выбора окружения | editors |
| Тестирование | место тестов в потоке | testing |
| Принципы и чистый код | связь принципов с практикой | principles |
| CI/CD, контейнеры, облака | анатомия конвейера | devops |
| Производительность | поиск узкого места | performance |
| Распределённые системы | что ломается при разделении | distributed-systems |
| Надёжность, SLO, инциденты | взгляд прикладного инженера | SRE |
| Безопасность приложений | секреты и стыки | security |
| Платформа и DX | окружение как продукт | platform-engineering |
| Роли, процессы, карьера | контекст вокруг инженера | sdlc-and-career |
Полная карта всех треков и рекомендованный порядок прохождения — в дорожной карте.
5. Четыре решения, которые дороже всего менять
Архитектура — это множество решений, которые дорого пересматривать. Их немного, и почти все они принимаются в первые недели проекта, когда информации меньше всего. Полезно знать их заранее, чтобы хотя бы принять осознанно.
1. Модель данных. Схема переживает код. Приложение можно переписать за квартал, а неудачно спроектированную структуру данных с миллионами строк придётся мигрировать годами, с обратной совместимостью на каждом шаге. Отсюда правило: думать о модели дольше, чем о фреймворке.
2. Граница между сервисами. Проведённая по неверному месту граница превращается в распределённый монолит: сервисы формально отдельные, но любое изменение требует согласованного релиза трёх из них. Граница должна совпадать с границей изменения, а та — почти всегда с границей команды и домена (закон Конвея).
3. Контракт, отданный наружу. Внутреннюю функцию можно переименовать за пять минут. Публичный API, которым пользуются чужие клиенты, живёт годами и умирает медленно, через deprecation и sunset-заголовки.
4. Способ хранения состояния. Решение «состояние живёт в памяти процесса» кажется безобидным ровно до момента, когда понадобился второй экземпляр приложения.
Обратите внимание на нижнюю часть графика: споры о форматировании и логгере занимают непропорционально много времени команд именно потому, что в них легко участвовать. Правильный ответ — автоматизировать и забыть; об этом в главе «Качество в потоке».
6. Правило по умолчанию: начинайте с простого
У трека есть сквозная позиция, и её честнее заявить сразу.
Монолит по умолчанию. Не потому, что микросервисы плохи, а потому, что разделение покупается операционной сложностью: сеть между вызовами, распределённые данные, сквозная трассировка, отдельные конвейеры, дежурства. Эти расходы оправданы, когда есть настоящая сила, толкающая к разделению — независимый релизный цикл, радикально разный профиль нагрузки, организационная граница. Мода такой силой не является. Подробный разбор с критериями — в главах 05 и 12.
Скучные технологии. У команды есть ограниченный бюджет на новизну. Каждая незнакомая технология тратит его — и когда в три часа ночи падает прод, знакомая PostgreSQL стоит дороже модного хранилища. Эта мысль хорошо сформулирована в эссе Дэна Маккинли «Choose Boring Technology» (https://mcfunley.com/choose-boring-technology).
Сначала измерить, потом чинить. Интуиция инженера о том, где узкое место, ошибается чаще, чем угадывает. Правило распространяется и на производительность, и на качество, и на процесс. См. «Масштабирование» и performance / измерение.
Обратимость важнее правильности. Решение, которое можно откатить за десять минут, можно принять быстро. Решение, которое нельзя откатить, требует обсуждения и записи. Отсюда популярность фича-флагов, канареечных выкатов и ADR.
7. Как устроен трек
Четырнадцать глав делятся на три блока. Первый — как система устроена, второй — как она создаётся и меняется, третий — как она живёт.
анатомия и карта"] subgraph S1["Блок I · Как система устроена"] C1["01 · Клиентская сторона"] C2["02 · Серверная сторона"] C3["03 · Слой данных"] C4["04 · Интеграция"] C5["05 · Архитектурные шаблоны"] end subgraph S2["Блок II · Как система создаётся"] C6["06 · Контроль версий"] C7["07 · Рабочее окружение"] C8["08 · Сборка и зависимости"] C9["09 · Качество в потоке"] C10["10 · CI/CD"] end subgraph S3["Блок III · Как система живёт"] C11["11 · Конфигурация и окружения"] C12["12 · Масштабирование"] C13["13 · Эксплуатация и эволюция"] end O --> C1 --> C2 --> C3 --> C4 --> C5 --> C6 C6 --> C7 --> C8 --> C9 --> C10 --> C11 C11 --> C12 --> C13
Блок I — как система устроена.
- Клиентская сторона — недоверенная среда, толстый и тонкий клиент, что можно и что нельзя класть на клиент, состояние на клиенте.
- Серверная сторона — жизненный цикл запроса, модели конкурентности, stateless, таймауты и пределы ресурсов.
- Слой данных — что даёт СУБД, как выбирать хранилище, пул соединений, ORM и N+1, миграции схемы.
- Интеграция — контракт, стили API, ошибки, ретраи и идемпотентность, эволюция без поломки клиентов.
- Архитектурные шаблоны на практике — слои и ярусы, каналы и фильтры, клиент-сервер, MVC, событийная архитектура, микросервисы; процедура выбора и ADR.
Блок II — как система создаётся.
- Контроль версий — зачем VCS, распределённая модель, снимки вместо патчей, хороший коммит, ветвление как решение о процессе.
- Рабочее окружение — воспроизводимый локальный стенд, версии рантайма, docker compose, IDE и отладка, инструменты проектирования.
- Сборка и зависимости — манифест и lock-файл, SemVer, транзитивные зависимости, воспроизводимая сборка, артефакты и кэш.
- Качество в потоке — лестница автоматических ворот, линтеры и типы, тесты в CI, покрытие, флаки-тесты, ревью.
- CI/CD — граница между integration, delivery и deployment, анатомия конвейера, фича-флаги, стратегии выката, метрики DORA.
Блок III — как система живёт.
- Конфигурация и окружения — одна сборка и пять окружений, иерархия источников конфигурации, секреты и их ротация, дрейф.
- Масштабирование — куб масштабирования, поиск узкого места, эволюция от MVP до распределённой системы и обратно.
- Эксплуатация и эволюция — наблюдаемость, инциденты, постмортемы, техдолг, легаси и удушающая смоковница.
8. Три маршрута чтения
Трек рассчитан на последовательное чтение, но не обязан читаться так.
Если вы junior и хотите увидеть картину целиком — читайте подряд, 00 → 13. Главы намеренно не требуют предварительного знания конкретных технологий: там, где нужна база, стоит ссылка на трек intro.
Если вы уже пишете код, но не понимаете, что происходит после git push — начните с
блока II: 06 → 07 → 08 → 09 → 10, затем 11. Это самая частая дыра у людей, пришедших из
учебных проектов: код писать умеют, довести до пользователя — нет.
Если вы проектируете новую систему или разбираете старую — 05 → 03 → 04 → 12 → 13. Это маршрут про форму системы, её данные, её стыки и её будущее.
9. Что считать результатом
Проверка усвоения — не «знаю термины», а «могу ответить на вопросы».
- Куда я положу эту логику: на клиент или на сервер, и почему?
- Что произойдёт с этим запросом, если база ответит за 30 секунд вместо 30 миллисекунд?
- Какое хранилище подходит под эту форму запросов и этот объём?
- Что случится с клиентами, если я перееименую поле в ответе API?
- Сколько экземпляров моего приложения можно запустить прямо сейчас, ничего не меняя?
- Через сколько минут после коммита я узнаю, что сломал сборку?
- Что именно я увижу в логах и метриках, когда это упадёт в три часа ночи?
- Что я откачу первым действием, если выкат оказался плохим?
Если на все восемь есть внятный ответ — трек сделал свою работу.
Мини-итог
Разработка — это не набор технологий, а три контура обратной связи вокруг одной системы: контур кода, контур поставки и контур эксплуатации. Клиент отвечает за удобство, сервер — за истину, хранилище — за долговечность, контракт — за возможность меняться независимо. Форма системы (монолит, сервисы, события) — это решение о стоимости изменения, а не о современности. Всё остальное в этом треке — детали того, как эти утверждения работают на практике.
Источники
- Len Bass, Paul Clements, Rick Kazman. Software Architecture in Practice, 4th ed. — каноническая схема разбора шаблонов «контекст / задача / решение».
- Martin Fowler. Patterns of Enterprise Application Architecture — https://martinfowler.com/eaaCatalog/
- Nicole Forsgren, Jez Humble, Gene Kim. Accelerate — эмпирическая связь инженерных практик с результатами бизнеса.
- Dan McKinley. Choose Boring Technology — https://mcfunley.com/choose-boring-technology
- Martin Fowler. Bliki — https://martinfowler.com/bliki/ (короткие статьи по большинству тем этого трека).
Что дальше
Начнём с той части системы, которую видит пользователь, и с главного правила стыка: клиент отвечает за отклик, сервер — за истину.