Системный подход к Backend-разработке: архитектура, инфраструктура и управление

Курс для Middle-разработчиков, направленный на формирование комплексного видения продукта. Охватывает проектирование систем, информационную безопасность, DevOps-практики и навыки командного взаимодействия.

1. Архитектура систем и продвинутая работа с данными

Архитектура систем и продвинутая работа с данными

Представьте, что вы проектируете систему для крупного онлайн-ритейлера в «Черную пятницу». Нагрузка возрастает в 50 раз за секунду. В этот момент разница между «просто кодом» и «архитектурой» становится физически ощутимой: либо система плавно масштабируется, либо база данных «умирает», утягивая за собой весь бизнес. Для Middle-разработчика переход на следующий уровень начинается с понимания того, что код — это лишь малая часть системы, работающей в условиях ограничений сети, памяти и дискового ввода-вывода.

Дилемма декомпозиции: Монолиты и Микросервисы

Выбор между монолитом и микросервисами — это не выбор между «старым» и «модным», а стратегический компромисс между скоростью разработки и сложностью эксплуатации. Монолитная архитектура обладает преимуществом строгой согласованности (ACID) и простоты отладки. Однако по мере роста команды монолит превращается в «большой ком грязи», где изменение в модуле оплаты может неожиданно сломать генерацию отчетов.

Микросервисы решают проблему масштабирования команд и технологий, но вводят налог на распределенность. Основная сложность здесь — сетевые задержки и риск частичных отказов. Если сервис А вызывает сервис Б, а тот не отвечает, сервис А должен иметь стратегию выживания (Circuit Breaker), иначе возникнет каскадный сбой.

> Закон Конвея гласит: организации проектируют системы, которые копируют их структуру коммуникаций. Если у вас три независимые команды, вы, скорее всего, придете к трем микросервисам. > > Melvin Conway, 1967

При переходе к микросервисам критически важно определить границы контекстов (Bounded Contexts). Например, в системе доставки еды «Заказ» в контексте кухни — это список ингредиентов, а в контексте логистики — это точка на карте и вес. Попытка создать единую модель «Заказа» для всей компании — главная ошибка, ведущая к созданию распределенного монолита, который сочетает недостатки обоих подходов.

Масштабирование данных и теорема CAP

Когда данных становится слишком много для одного сервера, мы прибегаем к репликации и шардированию. Репликация (Master-Slave) позволяет распределить нагрузку на чтение, но создает проблему задержки репликации (Replication Lag). Пользователь может обновить профиль, нажать «сохранить», обновить страницу и увидеть старые данные, потому что запрос на чтение попал на реплику, которая еще не синхронизировалась.

Шардирование (горизонтальное партиционирование) распределяет данные по разным узлам на основе ключа шардирования. Выбор этого ключа — самое ответственное решение. Если вы шардируете пользователей по user_id, данные распределятся равномерно. Но если вы выберете country_id, то шард с Китаем или США будет перегружен, а шард с Лихтенштейном — простаивать.

В распределенных системах мы всегда сталкиваемся с Теоремой CAP, которая утверждает, что при наличии сетевого разделения () мы можем обеспечить либо строгую согласованность (), либо доступность ().

| Сценарий | Выбор | Обоснование | | :--- | :--- | :--- | | Банковский перевод | Consistency (C) | Лучше выдать ошибку, чем позволить дважды потратить одни и те же деньги. | | Лента соцсети | Availability (A) | Пользователь переживет, если увидит пост на секунду позже, но он не переживет «белый экран». | | Регистрация ника | Consistency (C) | Нельзя допустить создание двух аккаунтов с одинаковым уникальным именем. |

Оптимизация SQL и NoSQL: когда индексов недостаточно

Многие разработчики считают, что добавление индекса решает все проблемы. Но индексы — это палка о двух концах: они ускоряют SELECT, но замедляют INSERT и UPDATE, так как дереву индекса (B-Tree) требуется перестройка. На больших объемах данных важно понимать разницу между типами индексов. Например, B-Tree идеален для поиска по диапазонам (), а Hash-индекс — только для точного совпадения.

В NoSQL решениях, таких как Cassandra или MongoDB, мы часто жертвуем нормализацией ради скорости. Если в SQL мы делаем JOIN трех таблиц, то в NoSQL мы храним данные в денормализованном виде, заранее подготавливая их для конкретного запроса. Это подход "Query-First Design": мы проектируем структуру данных под нужды UI, а не под абстрактную логику предметной области.

Рассмотрим пример: система комментариев. В SQL это таблица с parent_id. Чтобы достать дерево комментариев, нужны рекурсивные запросы или сложные CTE. В NoSQL (например, Document Store) мы можем хранить весь тред комментариев как один документ. Чтение происходит мгновенно, но обновление одного комментария в середине огромного документа становится дорогой операцией.

Разбор кейса: Проектирование системы уведомлений

Предположим, нам нужно спроектировать систему, отправляющую 1 млн уведомлений в час (Push, Email, SMS).

  • Прием запроса: API-сервис принимает запрос на отправку. Он не должен сам отправлять письмо, иначе клиент будет ждать ответа 5 секунд. Сервис кладет сообщение в брокер очередей (RabbitMQ или Kafka) и возвращает 202 Accepted.
  • Обработка: Воркеры (Consumers) забирают сообщения из очереди. Здесь важна идемпотентность. Если воркер упал после отправки SMS, но до того, как подтвердил получение сообщения из очереди (ACK), сообщение вернется в очередь и будет обработано снова. Воркер должен проверить в БД (например, в Redis по request_id), не отправлялось ли это сообщение ранее.
  • Хранение: Нам нужно хранить историю. SQL подойдет для метаданных, но для быстрого поиска по тексту уведомлений лучше использовать Elasticsearch.
  • Масштабирование: Если очередь растет, мы просто добавляем больше воркеров. Это классическое горизонтальное масштабирование.
  • Частая ошибка — использование базы данных в качестве очереди. Запросы вида SELECT * FROM tasks WHERE status='NEW' LIMIT 1 FOR UPDATE создают огромную нагрузку на блокировки в БД и не масштабируются. Специализированные брокеры используют структуры данных, оптимизированные именно для последовательного доступа.

    Архитектурные антипаттерны

    Один из самых опасных антипаттернов — Shared Database (общая база данных для разных микросервисов). Это убивает всю независимость: команда А меняет тип колонки, и у команды Б внезапно падает сервис. Каждый сервис должен владеть своими данными, а взаимодействие должно идти только через API или события.

    Другой нюанс — преждевременная оптимизация. Не стоит внедрять шардирование и Kafka, если у вас 100 пользователей. Сложность поддержки такой системы «съест» все ресурсы команды. Хорошая архитектура — это та, которая позволяет системе эволюционировать. Начните с монолита с четкими границами модулей, и когда нагрузка вырастет, вы сможете легко вынести нагруженный модуль в отдельный сервис.

    > Если вы не можете построить качественный монолит, вы не сможете построить качественную микросервисную систему. Сложность распределенных систем в разы выше.

    2. Безопасность и защита данных в бэкенде

    Безопасность и защита данных в бэкенде

    Для Middle-разработчика безопасность часто кажется «задачей того парня из отдела ИБ». Однако реальность такова: 90% уязвимостей закладываются на этапе написания кода и проектирования API. Если ваша система хранит пароли в открытом виде или подвержена SQL-инъекциям, никакие файрволы не спасут. Безопасность — это не состояние, а процесс минимизации рисков на каждом слое приложения.

    Анатомия угроз: OWASP Top 10

    Проект OWASP регулярно публикует список самых критичных рисков для веб-приложений. На первом месте часто оказываются инъекции (не только SQL, но и NoSQL, OS Command injection). Суть любой инъекции — смешивание данных пользователя с командами интерпретатора.

    Пример: если вы строите запрос как "... WHERE id = " + user_id, хакер передаст 1 OR 1=1, и получит доступ ко всем записям. Решение всегда одно — параметризованные запросы (Prepared Statements). В этом случае драйвер БД сначала отправляет шаблон запроса, а затем данные, которые никогда не будут исполнены как код.

    Другая важная угроза — Broken Access Control (нарушение контроля доступа). Это ситуация, когда пользователь может получить доступ к чужим данным, просто поменяв ID в URL. > Представьте API: GET /api/orders/123. Если злоумышленник меняет 123 на 124 и видит чужой заказ — это критическая дыра. Проверка прав (Ownership check) должна быть встроена в каждый эндпоинт на уровне бизнес-логики, а не только на входе в систему.

    Аутентификация и современный JWT

    Аутентификация отвечает на вопрос «кто ты?», а авторизация — «что тебе можно?». Сегодня стандартом де-факто является JWT (JSON Web Token). Он позволяет передавать состояние пользователя между микросервисами без постоянных походов в БД. Однако у JWT есть «подводные камни»:

  • Хранение секретов: Если ключ подписи токена утечет, любой сможет подделать личность администратора.
  • Отзыв токенов: Поскольку JWT самодостаточен, его сложно отозвать до истечения срока (TTL). Решение — использовать короткоживущие access_token и refresh_token, которые хранятся в защищенном хранилище (например, Redis) для возможности мгновенной блокировки сессии.
  • Никогда не храните JWT в localStorage браузера — это делает их уязвимыми для XSS-атак. Используйте HttpOnly куки, которые недоступны для JavaScript.

    Шифрование: как не облажаться с криптографией

    Главное правило разработчика: никогда не пишите свою криптографию. Используйте проверенные библиотеки (например, Libsodium или стандартные модули языка).

    Для паролей нельзя использовать обычные хэш-функции вроде MD5 или SHA-256. Они слишком быстрые, что позволяет злоумышленнику перебирать миллионы вариантов в секунду (Brute-force). Используйте «медленные» алгоритмы с солью (salt): Argon2, bcrypt или scrypt. Соль защищает от использования «радужных таблиц» (баз заранее вычисленных хэшей).

    | Тип данных | Метод защиты | Почему это важно | | :--- | :--- | :--- | | Пароли | Хэширование (Argon2/bcrypt) | Невозможно восстановить оригинал даже при утечке БД. | | Персональные данные (ПДн) | Симметричное шифрование (AES-256) | Нужно для отображения данных пользователю, но скрыто от посторонних. | | Передача данных | TLS (HTTPS) | Защита от перехвата данных провайдером или в публичном Wi-Fi. |

    Управление секретами и Environment Variables

    Частая ошибка — хранение паролей от БД или API-ключей в коде (Hardcoding) или в файлах .env, которые случайно попадают в Git. Как только секрет попал в историю коммитов, считайте его скомпрометированным.

    Для профессиональной работы используйте специализированные хранилища секретов: HashiCorp Vault, AWS Secrets Manager или Google Secret Manager. Приложение при старте запрашивает нужные ключи по временному токену. Это позволяет реализовать ротацию секретов: ключи меняются каждые 30 дней автоматически, и даже если старый ключ утечет, он быстро станет бесполезным.

    Разбор кейса: Защита от Brute-force и DoS

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

  • Rate Limiting: Ограничьте количество запросов с одного IP (например, не более 5 попыток в минуту). Это легко реализуется через Redis (алгоритм Token Bucket).
  • Captcha: Внедряйте проверку "человек или робот" после 3-й неудачной попытки.
  • Account Lockout: Временно блокируйте аккаунт после серии неудач. Будьте осторожны: это само по себе может стать вектором DoS-атаки (злоумышленник заблокирует всех пользователей). Лучше использовать «экспоненциальную задержку» (каждая следующая попытка требует больше времени ожидания).
  • Безопасность — это многослойный пирог. Если один слой (например, файрвол) пробит, следующий (аутентификация) должен устоять. Если пробита и она, данные внутри БД должны быть зашифрованы так, чтобы их нельзя было прочитать без ключа, хранящегося в Vault.

    3. Инфраструктура: от контейнеров до CI/CD

    Инфраструктура: от контейнеров до CI/CD

    В современной разработке фраза «у меня на локалке всё работает» больше не является оправданием. Бэкенд-разработчик сегодня должен понимать, как его код упаковывается, доставляется и запускается в промышленной среде. Граница между Dev и Ops размылась: вы не просто пишете функции, вы описываете среду их исполнения.

    Контейнеризация: Почему Docker — это не виртуальная машина

    До появления Docker мы настраивали серверы вручную: устанавливали нужную версию Python, библиотеки, зависимости. Это приводило к конфликтам («сервису А нужен OpenSSL 1.1, а сервису Б — 3.0»). Контейнеры решили эту проблему, изолируя приложение вместе со всем его окружением.

    Важно понимать: контейнер — это не легковесная виртуальная машина.

  • Виртуальная машина (VM) эмулирует железо и запускает целую гостевую ОС. Это долго и ресурсозатратно.
  • Контейнер использует ядро хостовой ОС, изолируя процессы с помощью механизмов Linux: namespaces (изоляция ресурсов типа сети и диска) и cgroups (ограничение потребления CPU и RAM).
  • При написании Dockerfile следуйте принципу многоэтапной сборки (Multi-stage builds). На первом этапе вы компилируете код (нужны компиляторы, SDK), а на втором — просто копируете бинарный файл в чистый, минимальный образ (например, Alpine Linux). Это уменьшает размер образа с 1 ГБ до 50 МБ, что ускоряет деплой и повышает безопасность (меньше лишнего софта — меньше дыр).

    Оркестрация: Когда одного Docker мало

    Если у вас один сервер и три контейнера, вам достаточно docker-compose. Но что делать, если серверов сто, а контейнеров тысячи? Здесь в игру вступает Kubernetes (K8s). Его задача — следить за тем, чтобы «желаемое состояние» системы совпадало с «действительным».

    Если вы сказали K8s: «я хочу 5 копий сервиса API», и один сервер сгорел, оркестратор сам перезапустит недостающие копии на других серверах. Основные абстракции K8s, которые должен знать бэкенд-разработчик:

  • Pod: Минимальная единица, в которой живет один или несколько контейнеров.
  • Service: Постоянный IP-адрес для группы подов (балансировщик внутри кластера).
  • ConfigMap/Secret: Способ прокинуть настройки и пароли в контейнер без изменения его образа.
  • CI/CD: Конвейер доставки ценности

    CI (Continuous Integration) — это практика частого слияния кода в общую ветку, сопровождаемая автоматическими тестами. Если вы запушили код, и тесты упали — сборка не проходит. Это гарантирует, что основная ветка всегда работоспособна.

    CD (Continuous Deployment/Delivery) — автоматизация выпуска продукта.

  • Delivery: Код готов к деплою, но кнопку «выпустить» нажимает человек.
  • Deployment: Каждое изменение, прошедшее тесты, автоматически попадает на продакшн.
  • Современный пайплайн выглядит так: Linting (проверка стиля) -> Unit-tests -> Security Scan (поиск уязвимостей в зависимостях) -> Build Image -> Integration Tests -> Deploy.

    > Индикатор зрелости команды: сколько времени проходит от коммита до появления фичи у пользователя? В топовых компаниях это занимает от 15 до 30 минут.

    Стратегии деплоя: Как не уронить систему

    Обновление версии приложения — самый рискованный момент. Существует несколько стратегий, минимизирующих простой (Downtime):

    | Стратегия | Механизм | Плюсы | Минусы | | :--- | :--- | :--- | :--- | | Rolling Update | Постепенная замена старых подов на новые. | Нет простоя, экономия ресурсов. | В моменте работают две разные версии кода. | | Blue-Green | Рядом со старой версией (Blue) поднимается полная копия новой (Green). | Мгновенное переключение, легкий откат. | Требует в 2 раза больше ресурсов. | | Canary | Новая версия катится на 5% пользователей. | Минимум риска: если есть баг, его увидят немногие. | Сложная настройка маршрутизации трафика. |

    Для бэкенд-разработчика Rolling Update означает, что код должен поддерживать обратную совместимость. Если новая версия кода уже запущена, а база данных еще не мигрировала (или наоборот), система не должна падать.

    Наблюдаемость (Observability)

    Запустив код в облаке, вы теряете возможность «просто посмотреть в консоль». Вам нужны три столпа наблюдаемости:

  • Метрики (Prometheus/Grafana): Цифры. Сколько запросов в секунду? Какая загрузка CPU?
  • Логи (ELK Stack: Elasticsearch, Logstash, Kibana): События. Что именно произошло, когда упала ошибка 500?
  • Трейсинг (Jaeger/OpenTelemetry): Путь запроса. Если запрос прошел через 5 микросервисов и тормозит, трейсинг покажет, на каком именно этапе возникла задержка.
  • Без этих инструментов отладка в распределенной системе превращается в гадание на кофейной гуще.

    4. Эффективное взаимодействие с Frontend и Mobile

    Эффективное взаимодействие с Frontend и Mobile

    Бэкенд не существует сам по себе — он обслуживает клиентов. Часто конфликт между «фронтами» и «бэками» возникает из-за плохо спроектированных API. Middle-разработчик должен понимать, что API — это контракт, и от того, насколько он удобен, зависит производительность всего приложения и батарейка смартфона пользователя.

    Проектирование API: REST, gRPC или GraphQL?

    Выбор протокола зависит от потребителя и условий сети.

    REST — самый популярный формат. Он прост, кэшируется браузерами и понятен всем. Однако у него есть две проблемы: Overfetching (сервер отдает лишние данные) и Underfetching (нужно сделать 5 запросов, чтобы собрать одну страницу).

    GraphQL решает эти проблемы, позволяя клиенту самому описывать структуру ответа. > Пример: мобильному приложению на слабом 3G нужно только имя и аватар пользователя. В REST оно получило бы весь профиль на 50 полей. В GraphQL клиент пишет запрос: { user { name, avatar } }, экономя трафик. > Но за это платит бэкенд: сложно кэшировать ответы и легко написать запрос, который «положит» базу данных (N+1 проблема).

    gRPC использует бинарный протокол Protocol Buffers. Он невероятно быстрый и эффективный, идеален для общения между микросервисами или для высоконагруженных мобильных приложений. Но его сложно отлаживать в браузере «глазами».

    | Технология | Где использовать | Главный плюс | | :--- | :--- | :--- | | REST | Публичные API, простые веб-сайты. | Стандартность и кэширование. | | GraphQL | Сложные интерфейсы, мобильные приложения. | Гибкость выборки данных. | | gRPC | Внутренние связи, Mobile-to-Backend. | Скорость и строгая типизация. |

    Жизненный цикл мобильных приложений и оффлайн

    В отличие от веба, мобильное приложение может внезапно потерять сеть. Бэкенд должен поддерживать стратегии синхронизации.

  • Optimistic UI: Фронтенд делает вид, что запрос прошел успешно, и обновляет интерфейс сразу. Бэкенд должен уметь обрабатывать конфликты, если запрос на самом деле упал.
  • Versioning: Вы не можете заставить всех пользователей обновить приложение одновременно. У вас всегда будут клиенты на версии v1.0, v1.1 и v2.0. Никогда не ломайте старые API. Используйте префиксы /v1/, /v2/ или заголовки версионирования.
  • Оптимизация передачи данных

    Для мобильных устройств критичен объем данных и количество запросов (каждый запрос «будит» радиомодуль, что тратит заряд).

  • Пагинация: Никогда не отдавайте списки целиком. Используйте limit/offset или, что лучше для бесконечных лент, Cursor-based pagination (передача ID последнего элемента). Это предотвращает дублирование постов при скролле, если в начало ленты добавились новые записи.
  • Сжатие: Убедитесь, что Gzip или Brotli включены на уровне Nginx.
  • BFF (Backend for Frontend): Паттерн, при котором для каждого типа клиента (Web, iOS, Android) создается свой мини-бэкенд. Он собирает данные из разных микросервисов и отдает их в том формате, который удобен конкретному клиенту.
  • Документация как контракт

    API без документации не существует. Swagger (OpenAPI) — стандарт индустрии. Он позволяет не только описать эндпоинты, но и генерировать клиентские библиотеки. > Хорошая практика: сначала пишется спецификация (Design-first), обсуждается с фронтенд-командой, и только потом начинается кодинг. Это позволяет фронтенду начать работу, используя «заглушки» (Mocks), не дожидаясь готовности бэкенда.

    Разбор кейса: Проблема N+1 в API

    Представьте, что вы отдаете список из 20 постов, и для каждого нужно вывести имя автора.

  • Плохо: Клиент получает список постов, а затем делает 20 запросов GET /users/{id}.
  • Чуть лучше: Бэкенд делает один запрос к постам, а потом в цикле делает 20 запросов к БД авторов. Это и есть N+1.
  • Правильно: Бэкенд делает один запрос с JOIN (в SQL) или использует DataLoader (в GraphQL), который собирает все ID авторов и делает один запрос WHERE id IN (...).
  • Эффективное взаимодействие — это когда бэкенд-разработчик думает о том, как его данные будут «рисоваться» на экране. Если для рендеринга одной кнопки фронтенду нужно вызвать три разных сервиса — архитектура API требует пересмотра.

    5. Методологии управления и командное лидерство

    Методологии управления и командное лидерство

    На уровне Middle+ разработка перестает быть только «написанием кода» и становится «решением проблем бизнеса в составе команды». Вы можете быть гениальным программистом, но если вы не умеете договариваться, оценивать сроки и понимать цели продукта, ваша эффективность для компании будет ограничена. Управление процессами — это не бюрократия, а способ снижения энтропии в условиях неопределенности.

    Agile и Scrum: Почему это часто не работает

    Большинство команд говорят, что работают по Agile, но на деле используют «карго-культ»: проводят стендапы, но не меняют процессы. Agile — это не набор ритуалов, а философия, описанная в манифесте: люди и взаимодействие важнее процессов и инструментов.

    Scrum — это жесткий фреймворк внутри Agile. Его ключевые элементы:

  • Спринты: Отрезки времени (1–4 недели), в конце которых должен быть работающий инкремент продукта.
  • Планирование: Команда берет на себя обязательства, исходя из своей скорости (Velocity).
  • Ретроспектива: Самый важный митинг. Команда обсуждает не «что сделали», а «как нам работать лучше». Если на ретроспективе все молчат — ваш Scrum мертв.
  • Главная ошибка — превращать стендап в отчет перед начальником. Стендап — это синхронизация разработчиков: «что мне мешает двигаться дальше?».

    Оценка задач и управление ожиданиями

    Почему разработчики всегда ошибаются в оценках? Потому что мы оцениваем идеальный сценарий, забывая про баги, митинги и «привет от админа».

  • Story Points: Оценивайте сложность, а не время. Сравнить «покрасить забор» и «построить дом» проще в относительных единицах, чем в часах. Используйте числа Фибоначчи (), чтобы подчеркнуть неопределенность: чем больше задача, тем меньше мы о ней знаем.
  • Конус неопределенности: В начале проекта ошибка в оценке может быть в 4 раза. По мере проработки требований точность растет. Никогда не давайте «жестких» дат на этапе идеи.
  • Коммуникация и лидерство без титула

    Лидерство — это не должность, а готовность брать на себя ответственность. Для Middle-разработчика это проявляется в:

  • Code Review: Это не поиск ошибок, а обмен знаниями. Не пишите «это плохо», пишите «как ты думаешь, что будет, если здесь придет null?».
  • Управление техдолгом: Бизнес всегда хочет «фичи на вчера». Задача лидера — аргументированно объяснить, почему сейчас нужно потратить 20% времени на рефакторинг, чтобы через месяц разработка не встала колом.
  • Soft Skills: Умение слушать и задавать вопросы. Часто 30-минутный разговор с заказчиком экономит неделю бесполезного кодинга.
  • Матрица Эйзенхауэра для разработчика

    Чтобы не выгореть и быть эффективным, нужно фильтровать задачи.

    | | Срочно | Не срочно | | :--- | :--- | :--- | | Важно | Критические баги, дедлайны. (Сделать немедленно) | Архитектура, обучение, техдолг. (Запланировать время) | | Не важно | Чужие мелкие просьбы, некоторые митинги. (Делегировать/Минимизировать) | Соцсети, бесконечные споры в чатах. (Удалить) |

    Ваш рост как Senior-разработчика происходит в квадрате «Важно — Не срочно». Именно там закладывается фундамент стабильной системы.

    Разбор кейса: Конфликт интересов "Бизнес vs Качество"

    Представьте: завтра релиз, но вы нашли потенциальную проблему с производительностью, которая может «положить» базу при 1000 юзерах. Менеджер требует релиза.

  • Реакция исполнителя: «Ок, я предупредил», — и катит код.
  • Реакция лидера: Собирает данные (цифры нагрузки), предлагает компромисс (например, выпустить фичу под Feature Flag только на 5% юзеров) и фиксирует план исправления в следующем спринте.
  • Лидерство — это умение находить баланс между «идеальным кодом» и «бизнесом, приносящим деньги». Код, который не приносит денег — бесполезен. Бизнес, который не заботится о коде — обанкротится из-за невозможности вносить изменения.

    Если из этого курса вы запомните три вещи, пусть это будут:

  • Думайте о системе в целом (CAP, сеть, диски), а не только о бизнес-логике.
  • Безопасность и инфраструктура — это часть вашей работы, а не «соседнего отдела».
  • Общайтесь с людьми больше, чем с монитором: лучшие архитектурные решения рождаются в диалоге.