Архитектоника в ИТ
315 subscribers
96 photos
6 files
95 links
Канал Александра Межова с заметками про ИТ-архитектуру и разработку.

ЛС @AlexanderMezhov

Сайт - https://amezhov.ru/blog/

MAX - https://max.ru/channel_arch_and_dev
Download Telegram
Словарь предметной области

Мне много раз приходилось знакомиться с новыми для себя проектами. Для ускорения понимания проекта и кода я каждый раз начинал с одного и того же: составлял словарь предметной области. 📔

И это не про графоманию, а про необходимость и реально рабочий инструмент – базис Domain-Driven Design – Ubiquitous Language. Чтобы общаться на иностранном языке, нужно пополнять словарный запас.

Вы можете обратиться к трудам Эрика Эванса, но не найдете там того, как именно должно выглядеть описание языка. Поэтому делюсь своим способом оформления и использования словаря.

Оформление

Формат словаря прост – это таблица из трёх колонок:

1️⃣ Термин – как должно быть.
2️⃣ Синонимы – как на самом деле в коде и моделях.
3️⃣ Определение – очень краткое толкование простым языком.

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

Например:
🕚Термин: Problem
🕚Синонимы: Task, Задача
🕚Определение: Задача по программированию

Использование

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

➡️ В коде нет единства именования. Не страшно, но это делает код более хаотичным и менее понятным; провоцирует развитие беспорядка и появление ошибок. Это сродни тому, когда иностранец говорит с акцентом, а абориген его плохо понимает.

➡️ Модель имеет неточность или какое-то упрощение. К сожалению, это распространенная проблема, и синонимы часто скрывают какие-то бизнес-процессы, с которыми вы ещё не столкнулись или не выявили. Подобные ошибки могут обойтись очень дорого, но словарь позволяет заметить эту проблему заранее.

❗️ Словарь не всегда прямолинеен: "термин – синонимы". Бывает, когда разные термины по факту имеют один и тот же синоним. И это уже очень серьёзный сигнал!

Например, в одном из проектов перечисление (enum) объединяло в один список два разных набора, которые использовались в двух разных бизнес-контекстах. Как например, если бы вы объединили в один список месяцы и дни недели. В реальной жизни эти списки используются по отдельности, но в коде они были объединены в один, который интерпретировался в каждой ситуации по-своему. Не трудно догадаться, какие проблемы в коде может породить подобное. Самое безобидное – это увеличение цикломатической сложности из-за обилия проверок и фильтров.

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

Хранение

Словарь предлагаю хранить в Markdown-файле в корне git-репозитория. И пусть корневой readme.md имеет ссылку на этот файл где-то в самом начале.

Не рекомендую использовать внешние wiki вроде Confluence по многим причинам. Во-первых, словарь должен отражать текущее положение дел, следовательно, требует версионирования. Во-вторых, нет необходимости доступа к внешним ресурсам, что удобно как для человека, так и для машины (AI). Хотите словарь в wiki – сделайте его копию, но оригинал пусть будет в репозитории.

Достоинства

1️⃣ Ускорение входа в проект. Это реально так: 30 минут на освоение словаря и вы готовы читать и понимать код и диаграммы, разговаривать на "птичьем" языке с коллегами по цеху. 🐥

2️⃣ Обнаружение неточностей в моделях. Словарь позволяет вскрывать проблемы моделирования и скрытые концепты.

3️⃣ Помощь в унификации именования в коде. В идеале все синонимы должны стать терминами.

4️⃣ Реальное подспорье для онбординга и AI-агентов. Думаю, что это становится крайне важно в современных реалиях.

🗂 Если у вас еще нет словаря, то у меня для вас отличная новость: хорошую заготовку можно легко и быстро сгенерировать с помощью AI. Например, используя такой промт.


И пусть ваш код говорит без акцента! ❤️

#arch #devops
Please open Telegram to view this post
VIEW IN TELEGRAM
7👍3🔥3
Consumer-Driven Contracts

Относительно давно интересуюсь темой Consumer-Driven Contracts и тестированием контрактов. В своём текущем проекте начал пробовать использовать Pact (и он уже помог найти несколько багов). И вот недавно мне попалась интересная подборка ссылок на эту тему.

🗂 Consumer-Driven Contracts и инструменты, автоматизирующие этот процесс, хорошо подходят при использовании MSA и в случае, когда в проекте есть важные интеграции, сломать которые было бы кране нежелательно.


Скажу честно, мне не удалось просмотреть весь найденный материал, но зато я актуализировал ссылки из оригинальной статьи. Результат решил оставить у себя на странице, чтобы никуда не пропало. 😏

➡️ Подборка материалов на тему Consumer-Driven Contracts ⬅️

От себя лично добавлю, что для входа в тему лучше почитать соответствующие главы из книги "Microservices Patterns" (Chris Richardson). Недавно вышло 2-е издание, а 1-е есть в русском переводе. Книга хорошо и пошагово разбирает многие нюансы разработки микросервисов, включая тестирование. Особенно полезно, если вы ведёте разработку на Java-стеке.

Более краткий и универсальный вариант изучения – это документация Pact. Pact поддерживает множество языков и, кажется, в своей документации они собрали самую лучшую и актуальную информацию на тему CDC. Кстати, иллюстрация к посту как раз с их сайта.

〰️〰️〰️

🔥 Также приглашаю вас 17-18 апреля принять участие в конференции Merge 2026, которая пройдёт в Казани (Иннополис). Промокод MEZHOV даёт скидку 20%. Также у меня есть 1 бесплатный билет, который я готов отдать тому, кто хочет и может побывать на этой конференции. Если что, то пишите в ЛС или в комментариях к посту. 😉

#tip
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥42
Неожиданный параллелизм при обработке сообщений в Kafka

Есть распространённое заблуждение, что в рамках одной консюмер-группы сообщения одной и той же партиции топика Apache Kafka обрабатываются последовательно, одно за другим. И конкурентный доступ к сообщениям партиции со стороны консюмера невозможен. Спешу вас разуверить, такое возможно, и вот вам моя история.

Рассмотрим последовательность событий:

1️⃣ Партиция MyTopic#0 назначена консюмеру MyConsumer#0.

2️⃣ MyConsumer#0 считал из партиции N сообщений.

3️⃣ MyConsumer#0 начал обработку сообщений.

4️⃣ Случилась ребалансировка.

5️⃣ Иных консюмеров нет.

6️⃣ Партиция MyTopic#0 вновь назначена MyConsumer#0.

7️⃣ MyConsumer#0 вновь считал из партиции те же сообщения.

8️⃣ MyConsumer#0 вновь начал обработку тех же сообщений.

➡️ Что тут может пойти не так?

Если консюмер осуществляет обработку событий в отдельном пуле потоков (worker threads pool), то копии считанных сообщений будут находиться в памяти клиентского приложения. Следовательно, в момент, когда консюмер после ребалансировки еще раз считает и начнёт обработку тех же самых сообщений, но в рамках другого потока из пула, возникает коллизия. Как минимум, могут существовать два потока, которые одновременно обрабатывают одни и те же сообщения.

➡️ Может ли такое произойти в вашем проекте?

В целом, может, и вот по какой причине.

С одной стороны, Kafka предлагает достаточно простой и универсальный API для обслуживания сообщений топика. Продюсер пишет сообщения, консюмер их читает, обрабатывает и сдвигает указатель на следующую пачку сообщений. Партиция, действительно назначается одному-единственному консюмеру в группе с гарантией, что только этот консюмер будет её обслуживать.

С другой стороны, Kafka API определяет событийную модель и предполагает, что все клиенты следуют некоторому предопределённому регламенту обработки этих событий. Из основных событий можно выделить назначение и отзыв партиций. То, как именно будут обрабатываться данные события, во многом определяется реализацией используемого Kafka Client или прикладным кодом. И тут начинается самое интересное.

В целом, основная ответственность ложится на прикладной код: будете ли вы обрабатывать момент ребалансировки, чтобы отменить уже запущенную обработку. И тут нужно ответить на два вопроса:

1️⃣ Насколько критично, если сообщение обработается дважды (idempotence)?

2️⃣ Насколько критично, если одни и те же сообщения будут обрабатываться параллельно (concurrency)?

К первому чаще всего многие готовы, а вот ко второму – нет.

К сожалению, в моём текущем проекте не был готов и я, ибо для работы с Kafka пришлось использовать проприетарную библиотеку, в которой не было никакой возможности обрабатывать момент ребалансировки. Конечно, сейчас я к этому готов, но осадочек остался, поскольку в какой-то степени понадеялся на фреймворк. 😮‍💨

Надеюсь, что эта небольшая, но поучительная история, поможет вам обойти стороной подобную проблему. ❤️

#dev #tip
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥8👍62
Важность авторского контроля

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

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

Такое бывает, в том числе, когда в команде нет ротации знаний, нет фиксации ADR, и каждый годами сидит на своём "участке". Естественно, что с уходом такого сотрудника, возникший коллапс начинает быстро заполняться чем-то новым и, как правильно, не содержащим принципиальных отличий. В таком случае можно сказать, что подобная ситуация вызвана недостатком знаний, но я считаю, что исходная причина в слабости архитектурных границ и отсутствии вектора развития.

Архитектура в том числе – вектор, направление развития. Если направления нет, то мы будем крутиться на месте, как корабль в полный штиль. В начале пути архитектор задаёт и корректирует этот вектор, поэтому очень важна обратная связь с разработкой, с помощью чего происходит уточнение и усиление направления движения.

К чему все эти рассуждения?

➡️ Во-первых, если вы даёте архитектору задание, а он в ответ выдаёт вам какой-то концепт, то пусть он за него и отвечает, сопровождая разработку до того момента, пока не будет достигнут хотя бы минимальный успех. В противном случае те идеи, те квадратики и стрелочки будут интерпретированы не так, как задумано, и все останутся недовольны.

➡️ Во-вторых, есть вещи, которые достаточно затруднительно передать в одних только диаграммах и ADR. Понимание общей концепции, взгляд со стороны, опыт, позволяющий понять, что началось отклонение от курса, всё это позволяет вовремя среагировать и не допустить ухода от цели.

К сожалению, был свидетелем, когда архитектора приглашали как "художника", а потом обижались, почему по его письменам получился не тот результат, на который рассчитывали. Был свидетелем, когда потеря направления приводила не к решению проблемы, а её перемещению из одного угла в другой. Всё это я связываю только с одним – отсутствием авторского контроля и, как следствие, непонимание направления движения, уход от цели и последующий хаос в действиях.

Почему это особенно важно сейчас?

➡️ С бурным развитием AI человеку – архитектору и разработчику – остаётся всё больше человеческой работы, а именно: следить за порядком и задавать направление развития. Агентская разработка приумножает, а мы определяем, что именно: хаос или порядок. Это во многом объясняет успешное сочетание AI со стратегическими шаблонами DDD.

Теперь, как мне кажется, нам очень важно не только навести порядок и заложить правильный курс, определив концепцию разработки в каком-нибудь CLAUDE.md или AGENTS.md, но и, как капитану корабля, неустанно следить за сохранением этого курса. И для каждой новой идеи эта навигация должна осуществляться до тех пор, пока все члены команды не начнут придерживаться общей цели.

Guide your force and may the order be with you! ❤️

#arch #ai
Please open Telegram to view this post
VIEW IN TELEGRAM
👍11🔥1
Chronicle Queue - Kafka на минималках

Что, если нужна простая и высокопроизводительная очередь, но вы не хотите усложнять решение путём добавления полноценного брокера? Или нужно обеспечить распределённую обработку огромного потока событий с непредсказуемыми всплесками активности? Сегодня поговорим еще об одном классе решений – локальные очереди. 🚀

Контекст

Имеется связка Producer-Consumer, в которой Producer может генерировать события намного быстрей, чем Consumer успевает их обрабатывать. При этом Producer отправляет события синхронно, а Consumer не должен замедлять его работу, вынуждая ждать окончания обработки или требуя повторной отправки в случае ошибки.

Примеры:

Высокочастотный источник, например, биржевые котировки – миллионы событий в секунду и/или случайные всплески активности. Нужно предоставить возможность быстрой записи событий, а их обработку делать в фоне.

При обработке событий производится обращение к ресурсу с низкой доступностью, но нужно гарантировать успешность обработки. Например, гарантировать запись в БД или вызов сервиса, с которым возможна потеря сетевого соединения. Иначе говоря, когда нужно пережить недоступность внешнего компонента.

Нужно обеспечить высокий уровень масштабируемости, параллелизма обработки и адаптируемости к нагрузкам. При этом порядок обработки событий в общем случае не важен. Например, система, подобная LeetCode, в которой агенты должны осуществлять проверку присылаемых решений во время проведения соревнований.

Проблема

Получив событие, Consumer сразу отвечает успехом, но добавляет задачу в очередь асинхронной обработки. С появлением очереди возникает целый комплекс проблем.

Где Consumer будет хранить необработанные события? Если в оперативной памяти, то можно быстро дойти до OOM/Killer. Если использовать брокер сообщений, то какой?

Если Kafka, то как адаптировать её под всплески активности и выбрать оптимальное количество партиций, fetch size, poll timeout и т.п.? А если в системе нет никакого брокера и его добавление крайне нежелательно или преждевременно? Например, не нужны все возможности Kafka, а её добавление выливается в огромные траты по сопровождению.

Решение

Суть решения:

➡️ Consumer использует персистентную очередь, которую хранит в локальной файловой системе. Получив от Producer событие, Consumer сохраняет её в файл очереди – append-only-лог – и тут же отвечает успехом.

➡️ Обработку событий из файла очереди Consumer выполняет в отдельном потоке или процессе. Если событие требует принципиально разной обработки, Consumer может запустить разные обработчики в разных потоках или в отдельных процессах.

Дополнительно нужно учесть:

⚠️ Каждый экземпляр Consumer должен иметь свой собственный файл очереди. Соответственно, в Docker нужно настроить Volumes; в Kubernetes – PV. Это позволит не потерять события при перезапуске Consumer.

⚠️ Желательно, чтобы Consumer реализовывал Graceful Shutdown, гарантируя обработку оставшихся сообщений в очереди до прекращения своей работы. Это позволит адаптировать количество обработчиков как в большую, так и в меньшую сторону.

⚠️ Для обеспечения микросекундных задержек, а также для возможности конкурентного доступа к файлу очереди, можно использовать Memory-mapped files.

Шаблон реализуется самостоятельно, с помощью специальных библиотек, файловых или embedded-баз данных (RocksDB, SQLite, LiteDB и т.п.).

🔥 Для JVM/Java/Kotlin отличным решением может быть Chronicle Queue (при запуске в Java 17+ для JVM нужно указать дополнительные настройки).

Плюсы

👍 Концептуальная простота решения.

👍 Масштабируемость, адаптируемость, производительность.

👍 Высокий уровень параллелизма и распределённой обработки.

👍 Нет сетевых издержек на общение с брокером.

👍 Простота деплоя и сопровождения.

Минусы

👎 Нет гарантий порядка обработки.

👎 Риск потери части событий при выходе из строя узла, на котором хранились файлы очередей. Снижать эти риски нужно самостоятельно.

👎 Нет партиционирования данных. Его нужно реализовывать самостоятельно, например, на уровне роутинга событий от Producer к Consumer.

#tip #arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥5👍2
📺 От разработчика к AI-агенту: единые правила игры

В мире разработки появился искусственный интеллект – катализатор, ускоряющий все процессы. AI действительно ускоряет разработку, но не делает её автоматически лучше – он просто масштабирует текущее состояние проекта. Если в коде царит хаос, AI поможет быстрее его размножить; если есть структура – он начнёт её усиливать. Существуют ли практики, которые позволят заложить основу, способную одинаково хорошо работать как для людей, так и для AI-агентов? Позволят ли они ускорить процесс передачи (управления)?

🕒 12 мая в 16:00 МСК приглашаю на онлайн-митап, на котором я поделюсь своим опытом проектирования AI-контекста. Расскажу, какие практики из DDD заиграли новыми красками и позволили мне значительно улучшить качество работы с AI.

Формат митапа не ограничен временем, количеством участников. Можно свободно материться высказываться и делиться опытом. Обещают, что запись будет, но чуть позже.

🗂 Кстати, данный митап является "приквелом" к моему предстоящему докладу на UWDC 2026 "Передача проекта глазами инженера", на который я таже приглашаю всех 16 мая. Если вы будете в это время в Челябинске, то обязательно приходите на UWDC – это крупнейшая ежегодная ИТ-конференция нашего региона, которая всегда отличалась хорошим качеством контента.


🥰 Ссылка на Google-клендарь

😊 Ссылка на Yandex-календарь

📱 Ссылка на трансляцию (она же ссылка на запись)

#conf #uwdc
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥5👎1
Факторы принятия и пересмотра решений

Как вы принимаете архитектурные решения? Согласитесь, что вопрос непростой даже для человека с опытом. Давайте подумаем и попробуем ответить на этот вопрос структурно. 🔬

В общем случае я придерживаюсь примерно такой последовательности:

⬇️ Погружаюсь в контекст проблемы.
⬇️ Выясняю функциональные требования.
⬇️ Выясняю нефункциональные требования.
⬇️ Определяю важные архитектурные свойства.
⬇️ Выясняю ограничения и сроки реализации.
⬇️ Выясняю, какие решения уже приняты или реализованы.
⬇️ Выясняю, что не устраивает в существующем варианте.
⬇️ Определяю положение элемента в системе и его связи.
⬇️ Рассматриваю несколько возможных вариантов.
⬇️ При необходимости провожу R&D для их сравнения.
➡️ По совокупности факторов выбираю наиболее оптимальный.
⬇️ Описываю принятое решение и как оно решает проблему.
⬇️ Описываю плюсы и минусы решения.
⬇️ Описываю риски, их важность и вероятность.
⬇️ Описываю способы снятия или смягчения рисков.
⬇️ Перечисляю рассмотренные решения и причины их отклонения.
⬇️ Указываю этапы реализации и их оценку.
⬇️ Описываю возможные дальнейшие шаги развития.
⬇️ Описываю способ осуществления архитектурного контроля.
⬇️ Презентую идею команде/комитету/руководителю/заказчику.
⬇️ Отрабатываю замечания и предложения.

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

Пройдя этот путь мы, наконец, приняли решение. Но принятое решение – это статика, точка в прошлом. Что насчет динамики, развития и пересмотра?

Как мне кажется, этот вопрос до сих пор остаётся открытым. Было бы здорово, если бы кто-то тебе напоминал: "Слушай, помнишь, ты принимал решение X? Пришло время его пересмотреть!"

➡️ Полагаю, к этому скоро придём, а сейчас я бы предложил в ADR опционально добавлять пункт "Критерии пересмотра решения".

Как минимум, можно обозначить следующие мотивы пересмотра принятых решений:

☑️ Появление новых архитектурных стилей. Например, повторное использование кода в монолитах часто начинает работать как антипаттерн, т.к. увеличивает связность. MSA позволила решить эту проблему.

☑️ Изменения в экосистеме. В мире всё меняется очень быстро. Отвергнутые или недопустимые решения могли стать лучше; возможно, появилось что-то более эффективное. Многие алгоритмы долгие годы пылились на полках в ожидании своего звёздного часа.

☑️ Изменения внешних условий. Условия лицензирования, стоимость подписки или аренды, доступность зарубежных ресурсов, необходимость импортозамещения, политические решения. Своевременная реакция может существенно повлиять на результат.

☑️ Изменение предметной области. Изменение законодательной базы, новый способ налогообложения, пересмотр бизнес-процессов в связи с появлением пресловутой ИИ и т.п. Если подобные вещи могут влиять на архитектурные свойства, решение требует пересмотра.

☑️ Новые возможности хранения. Например, поддержка JSON в ClickHouse эволюционировала постепенно, и долгое время JSON обрабатывали как строку, а не колоночный тип. Это создавало проблемы для тех, кто хотел OLAP для документов с вариативным набором атрибутов.

☑️ Изменение характера нагрузки. Даже если две системы делают одно и тоже, но одна рассчитана на 5k DAU, а другая – на 100k DAU, они будут иметь принципиально разную архитектуру. Например, система прохождения курсов по программированию и система проведения онлайн-олимпиад. В обеих случаях нужно проверять решения, но характер нагрузки совершенно разный.

☑️ Рост объема функций. Здравый смысл подсказывает: начинайте с модульного монолита. Когда разные модули имеют разные архитектурные свойства, нужно серьезно задуматься! Например, что-то нуждается в масштабировании, а что-то нет. Так появляются инсталляции с огромным Heap-ом, а сеньоры с умным видом начинают перебирать различные версии GC.

☑️ Рост количества связей. Когда хореография превращает проект в "клубок", не ждите, идите в оркестрацию.

☑️ Деградация связей. Начали с синхронного взаимодействия, уперлись в пропускную способность вызываемого сервиса, решили перейти на асинхронное.
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍2
Повод задуматься

Вы уже заметили, что у меня есть привычка делиться своими наблюдениями. Обычно нет какого-то чёткого контент-плана; идеи постов возникают в процессе работы, чтения книг, статей, информационных каналов. Сегодня не будет классического поста, сегодня будет список коротких тезисов – наблюдений, которые однажды заставили меня остановиться и задуматься. 😏

Про архитектуру

🗂 Самое важное в работе архитектора не в том, чтоб предложить правильное решение, а в том, чтобы обосновать его необходимость и целесообразность. Архитектура должна служить ответом на вопрос "зачем".

🗂 Архитектура должна задавать критерии отбора решений, то есть создавать не саму структуру, а условия, в которых она складывается.

🗂 Если решение отлично подходит и работает, то это компромисс, а не антипаттерн. Проблема в том, что многие думают, что их решение является компромиссом.

Про микросервисы

🗂 Распределённый (distributed) не значит расцепленный (decoupled).

🗂 Переиспользование кода – антипаттерн для микросервисов.

🗂 Независимый деплой – независимая деградация.

Про базы данных

🗂 После отказа от ACID всегда будет вероятность получить что-то посередине. И даже с ACID такая вероятность всё ещё остаётся.

🗂 Не нужно думать, что если вы используете ORM, вы отгородились или абстрагировались от БД.

Про тесты

🗂 Тесты нужны для проверки изменений в коде.

🗂 Тесты должны проверять функциональность, а не её реализацию.

🗂 Тесты на mock-ах часто проверяют, что код написан так, как написан, не более.

Про ИИ

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

🗂 С внедрением ИИ скорость разработки возрастает, но и объём работы увеличивается. Всё просто: чем быстрей делаешь задачу, тем быстрей тебе дают новую.

🗂 Промт должен определять не только как нужно делать, но и как не нужно. Желательно с примерами "как правильно" и "как неправильно". Но настоящая автоматизация начинается в тот момент, когда промт определяет не "как", а "что".

🗂 Заметный прирост производительности труда при использовании ИИ наблюдается в трёх случаях: 1) рутинная работа; 2) когда в основе генерации лежит качественный контекст и адаптированные процессы (но похоже, что для большинства это редкость); 3) когда пользователь плохо осведомлен о предмете генерации и не может объективно оценить качество запроса и ответа.

〰️〰️〰️

Это нерегулярная рубрика, но если вам зашел такой формат, поставьте реакцию. ❤️

#view #arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
👍12🔥8🤡1
Deadline Propagation

Знакома ситуация: переключаете нагрузку на развернутый сервис, а он ложится, и так по кругу? Почему-то в контексте распределённых систем про Deadline Propagation говорят намного реже, чем должны. Сегодня исправим эту несправедливость. 😏

Контекст

Имеется цепочка сетевых вызовов:
Client -> A -> B -> C -> ...


Вполне возможно, что клиент перестанет ждать ответа от такой системы раньше, чем она завершит свою работу. Предположим, что это случилось в момент, когда обработка находилась на этапе обработки на узле B:
Client --> A -> *B* -> C -> ...


В этом случае цепочка вычислений не прервется: узел B отработает и вызовет C и т.д. И только в момент, когда узел A попытается отправить ответ клиенту, возникнет ошибка.

Ситуация возможна в любой распределённой системе, в особенности, когда у клиента есть жесткий таймаут на получение ответа.

Проблема

Начиная с момента прерывания со стороны клиента, вся остальная цепочка вычислений становится бесполезной нагрузкой на систему и инфраструктуру. Хуже всего то, что после получения таймаута клиент может попытаться сделать retry и усугубит ситуацию, т.к. к уже существующей нагрузке добавится новая (load amplification).

Можно попытаться отменить вычисления, используя Cancellation, но этот подход имеет ряд существенных недостатков:

⚠️ Сложность реализации. Каждый узел в цепочке должен реализовать механизм отмены вычислений. Таким образом, проблема, которая должна решаться на инфраструктурном уровне, начинает решаться на прикладном.

⚠️ Cancel-запрос отправляется вдогонку. Во-первых, это еще один запрос, который нужно обработать. Если система уже нагружена, cancel-запросы только ухудшат ситуацию. Во-вторых, если cancel придет поздно, бесполезная работа уже будет сделана.

⚠️ Cancel-запрос может не достичь цели. Например, если cancel добежал до B, но между B и C нет сетевой связности, всё напрасно: C и вся остальная цепочка продолжит выполнять бесполезную работу.

Решение

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

Значение Deadline может быть:

➡️ Абсолютным (deadline timestamp) – точная дата и время, когда истекает срок. Преимущество – время работы захватывает сетевые издержки и возможные паузы (VM Pauses, GC Stop-the-World), а не только чистое время работы на узле. Это может быть особенно полезно, если клиенту важней контролировать общую продолжительность по wall clock, а не время вычислений. Недостаток – часы на узлах должны быть хорошо синхронизированы, ведь deadline timestamp сравнивается с текущим временем на узле исполнения. Если все узлы работают в общей инфраструктуре, то проблема решается синхронизацией через NTP.

➡️ Относительным (execution timeout) – сколько осталось время для оставшейся работы. Преимущество – не нужна строгая синхронизация часов. Недостаток – не учитываются сетевые издержки и возможные паузы. Этой концепции придерживается, например, gRPC. Решение допустимо для инфраструктуры, где издержки на вызовы минимальны; и наоборот, если нельзя допускать, чтобы запросы падали по Deadline всякий раз, когда инфраструктура немного лагает.

Никто не запрещает передавать и абсолютный, и относительный Deadline, комбинируя оба подхода.

Способ передачи Deadline зависит от транспорта. Например, в gRPC этот механизм встроен в протокол; в HTTP/Kafka/AMQP – custom-заголовки.

Плюсы

👍 Система перестает делать бесполезную работу.

👍 Нет амплификации нагрузки.

👍 Узлы работают автономно и сами принимают решение о прекращении.

👍 Логика обработки унифицирована и находится на инфраструктурном уровне.

Минусы

👎 Короткий Deadline может провоцировать нежелательные прерывания.

👎 Для большинства протоколов инфраструктурный код придётся писать самому.

〰️〰️〰️

Кстати, если кто-то не в курсе, пока мы думаем, как строить распределённые системы, Podlodka AI Crew рассказывает, как можно упростить этот процесс. 😃

#tip #arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
👍5🔥3
Три множителя для AI-ускорения

Минувшие две недели были крайне насыщенными: Podlodka AI, а вместе с этим подготовка и участие в TechLead. Всё это заняло большую часть моего свободного времени и отодвинуло все остальные активности на второй план. Но всё было не зря – концентрат полученной информации заставляет по-новому взглянуть на уже знакомые вещи или начать изучать что-то новое. 🚀

Вполне ожидаемо, что всё, что было на TechLead, так или иначе было вокруг темы ИИ, даже несмотря на отдельно выделенный ИИ-трек.

Ключевая мысль, которую я для себя вынес, выражается простой математической формулой:
AI_boost = AI_adoption × AI_infrastructure × SDLC


Иначе говоря, прирост производительности, который может дать ИИ, зависит от трёх множителей:

1️⃣ Степень использования ИИ среди сотрудников. Как часто и какой объем работ сотрудники компании делегируют ИИ-инструментам? Насколько хорошо они знакомы с такими инструментами и понимают их границы применимости? Ответственные – сотрудники и AI-амбассадоры компании.

2️⃣ Уровень развития ИИ-инфраструктуры в компании. Вам настроили VPN? Купили подписки? Обеспечили инструментарием? У вас есть базовый минимум, чтобы только начать пользоваться ИИ? Или каждый сотрудник обо всём этом должен заботиться сам? Ответственные – руководство компании.

3️⃣ Процесс разработки программного продукта. Он у вас вообще начал меняться? Вы начали переход на SDD-фреймворк? Начали настройку контекста и пайплайна? Описание бизнес-требований адаптируется под AI-friendly-формат (например, Markdown)? Начали задумываться об evals и quality gates? Ответственные – техлиды и архитекторы.

➡️ Обнули хотя бы один множитель и получишь ноль.

Это чистая математика – для нахождения вероятности наступления двух и более событий нужно умножить вероятности наступления каждого. Аналогичный подход используется для определения уровня доступности сервиса.

За формулу можно поблагодарить докладчика из Яндекса – Олега Смолякова, который выступил с темой "Как ИИзировать 10 тысяч инженеров" (презентация). С его слов они пытаются достичь 0.75 по каждому показателю. ☄️

Всё это, безусловно, увеличивает стоимость владения проектом. Хочешь ускорение, тогда не получится "как раньше", придётся подтягивать всё то, на что всегда не хватало времени. Вполне возможно, что для некоторых такая дисциплина может стать непосильной ношей, но двигаться в эту сторону всё равно придётся.

Продолжая тему, многие докладчики и участники конференции подтвердили необходимость пересмотра SDLC, выстраивания SDD-фреймворка, проектирования контекста и настройки пайплайна, который обязательно должен включать не только quality gates, но и fallback-стратегии на каждом шаге. Также пришли к практическому заключению, что использование большого количества параллельно работающих агентов (более 4) чаще всего приводит к деградации. В результате настраивают последовательный пайплайн, в котором каждый шаг выполняют, используя специализированную роль со своим набором skill-ов (архитектор, разработчик, тестировщик и т.д.).

〰️〰️〰️

Конечно, это не всё, о чём можно рассказать, но, пожалуй, это структура, на которой был построен этот сезон TechLead.

Второе важное отличие от предыдущих сезонов – это практическая направленность. Половина контента, как и обещали организаторы, была выполнена в виде воркшопов. 👨‍💻

Тем, кто интересуется темой конференций и хочет узнать, насколько хорош новый формат, скажу просто – он другой. Раньше 1 или 2 дня докладов, и у тебя голова кругом от обилия и плотности информации. Потом ты еще неделю отходишь от всего полученного. Сейчас 1 или 2 дня воркшопов – голова не пухнет, но ты знакомишься с каким-то узкоспециализированным навыком. Это не лучше и не хуже, просто прошлый формат рассчитан на широту знаний, а новый – на их глубину.

#view #ai #conf
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥4👍32
System Design: чьё кунг-фу сильней

Мы все в той или иной степени архитекторы. А для архитектора всегда была важна насмотренность и широта знаний. 💪 Но где её получить, сидя на одном проекте долгие годы? 😮‍💨 Как минимум, часть информации можно найти в книгах. Конечно, никто не будет досконально описывать архитектуры реальных систем, но принципы построения больших систем найти можно.

Если цель – расширить кругозор и понять базовое устройство крупных распределённых систем, лучше начать с книг по System Design (Interview). Формат подобных книг позволяет за раз рассмотреть несколько кейсов и увидеть, как теория применяется на практике.

Для обозначенной цели могу порекомендовать две книги:

🗂 System Design Interview - An Insider's Guide, Alex Xu, 320p (System Design. Подготовка к сложному интервью, Алекс Сюй, 304с)

🗂 Acing the System Design Interview, Zhiyong Tan, 472p (System Design: пережить интервью, Чжиюн Тань, 544с)

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

Книга Alex Xu ценна тем, что идёт от простого к сложному. Содержимое предыдущих глав используется в последующих. В итоге получается целостный материал, который будет понятен без особой предварительной подготовки. В каждом кейсе автор четко выдерживает формат повествования, к которому привыкаешь практически сразу. Кейсы описаны кратко, но в то же время достаточно понятно. В конце решения накидываются варианты возможных дополнительных вопросов, но практически не рассматриваются альтернативные архитектуры. Пожалуй, лаконичность и легкость подачи материала – главные драйверы успеха этой книги.

Книга Zhiyong Tan выполнена в похожем стиле, но требует от читателя гораздо больше подготовки. Иначе говоря, новичкам чтение может не принести пользу. В первой части автор заставляет вспомнить минимально необходимый базис, не пытаясь даже объяснить некоторые детали. Если Alex Xu пытается заполнить пробел, то Zhiyong Tan в конце каждой главы даёт ссылки на дальнейшее изучение. В связи с этим по началу не понимаешь, что происходит, но основная задумка в том, чтобы читатель понял, что ещё нужно изучить прежде, чем начинать что-то проектировать или пойти на интервью. Вторая часть посвящена разбору кейсов; и главы также логически взаимосвязаны. Ключевое и самое ценное в повествовании – это рассмотрение нескольких возможных архитектур и векторов развития. С этой целью от начала и до конца каждого кейса накидывается куча вопросов, которые должен задавать архитектор. Вместе с этим идеи некоторых возможных решений формулируются настолько кратко и небрежно, что без наличия опыта понять их будет очень трудно. По сути, книга активно демонстрирует архитектурное мышление и процесс поиска компромиссов.

➡️ По итогу обе книги расширяют кругозор, но вторая определяет уровень необходимых знаний и показывает настоящую работу архитектора.

〰️〰️〰️

Кстати, если вы решитесь купить книгу Zhiyong Tan в русском переводе, то сразу скажу, что перевод читается тяжело, а некоторые термины переведены крайне отвратительно. В этом отношении перевод книги Alex Xu выполнен почти идеально.

#view
Please open Telegram to view this post
VIEW IN TELEGRAM
6👍52
Работай с датой и временем правильно!

Можно бесконечно смотреть на три вещи: как горит огонь, течёт вода и как люди пытаются работать с датой и временем. 😃 Но одна из этих вещей мне окончательно надоела, поэтому я решил написать на эту тему целую статью. 👔

Если вы думаете, что знаете про время всё, то я в этом ничуть не сомневаюсь. 😏 Однако, по моим наблюдениям, даже среди технических специалистов, включая опытных разработчиков, достаточно много людей, которые не понимают базовых вещей либо имеют кучу стереотипов, в которые продолжают охотно верить. Это не проблема до тех пор, пока неумелое творчество таких коллег не касается лично вас. Надеюсь, что данная статья, как минимум, поможет вспомнить смешные и не очень истории из жизни или найти для себя что-то новое и незнакомое.

Получилось много, но надеюсь, будет интересно.

➡️ Ссылка на статью ⬅️

#arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
4👍1😁1
Контракт ошибок

Как часто мы совершаем ошибки? Постоянно! Что мы делаем в случае неудачи? 🍑 Стремимся её исправить, а если не получилось, решаем, как рассказать об этом заинтересованным лицам. Подбирая нужные слова, оцениваем размер ущерба и возможную реакцию на возникшую ситуацию. Вместе с этим исходный контекст может быть обобщён, изменён или расширен.

Допустим, вы так и не смогли выполнить задачу по интеграции с другой системой из-за недостатка информации. Но руководителю вместо голого факта говорите что-то в стиле: "Я запросил документацию у владельцев системы, они сегодня дадут ответ, и я уточню сроки реализации."

В такой форме мы передаём:

🟢 Контекст – "задача не выполнена"

🟢 Текущее состояние – "запросил документацию, жду ответ"

🟢 Масштаб последствий – "сроки реализации затягиваются"

🟢 Дальнейшие действия – "сегодня уточню сроки"

И это не вежливость, это управление ожиданиями, которое постоянно используется в быту. А в программных системах это называется контрактом ошибок.

Но несмотря на то, что программы ошибаются чаще людей, контракту ошибок уделяется не так много внимания. Возможно, из-за того, что в момент реализации API не было чёткой структуры действий; а возможно, из-за излишней сосредоточенности только на функциональности.

При проектировании API внимание должно быть уделено трём составляющим:

1️⃣ Набор операций

2️⃣ Модель данных

3️⃣ Контракт ошибок

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

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

Контракт ошибок есть, мониторинг есть, но оба бесполезны.


Почему так происходит? Всё как в обычной жизни: не подумали о реакции пользователя на ошибки.

➡️ Если собираетесь сообщить об ошибке, подумайте, что с ней будет делать пользователь вашего API, у которого гораздо меньше контекстной информации, чем у вас сейчас. Как именно вы хотите повлиять на его поведение?

За каждой ошибкой в любом API должна быть предусмотрена какая-то история по её анализу и обработке. Если такой истории нет, это признак плохого дизайна. Проверить полезность ошибки очень легко:

Может и должен ли пользователь API действовать как-то иначе, чем просто try-catch-log или try-catch-retry?


Если может – отлично, API определяет поведение пользователя; если нет – ошибка ничем не отличается от безликой Internal Server Error. Последнее может быть сигналом, что вам не нужна излишняя детализация в ответе. И это нормально, если всё, что вы ожидаете – это log или retry.

➡️ Излишняя детализация ошибочных ситуаций – это ещё одна ловушка, в которую можно угодить, руководствуясь добрыми намерениями. И чаще всего в эту ловушку попадают аналитики и тестировщики, демонстрируя, что они предусмотрели все возможные варианты использования.

Рассмотрим конкретный пример. Предположим, нужно отменить заявку записи на приём ко врачу. Предполагается, что клиент передает только ID заявки. Операция может завершиться неудачей только по двум причинам: передали несуществующий ID или заявка уже отменена. Для каждого такого случая можно было бы предусмотреть свою ошибку. Но будет ли отличаться поведение пользователя в первом и втором случае? Нет, и более того, в обеих случаях не предполагается retry.

А что, если вероятность передачи несуществующего ID крайне низкая, конкурентность записи высокая, а пользователь работает с API по принципу fire-and-forget? Возможно, что в такой ситуации и ошибку возвращать бессмысленно.

Ошибки – это неотъемлемая часть проектирования контракта взаимодействия, поэтому в центре дизайна должно стоять поведение, а не попытки рассказать, что именно пошло не так.

#view #arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
👍4🔥2
Тип поддомена и стратегия решения

Обычно DDD воспринимают как высшую математику: всё логично, но непонятно, как применять на практике. Сегодня предлагаю свести теорию с практикой и посмотреть, как определение поддомена определяет стратегию решения. 🎯

На самом деле стратегия не в том, чтобы пилить систему на поддомены; и не в самом факте существования поддомена. Стратегия – в выборе проектных решений, исходя из типа поддомена.

➡️ Проще говоря, правильно определив тип поддомена, сможете легко понять, работаете ли вы над чем-то стоящим или просто тратите время и ресурсы впустую.

Смотрим, как сориентироваться на местности, сделав три простых шага.

1️⃣ Введём понятие типа поддомена:

🕚 Основной (core). Уникальная деятельность компании (не обязательно техническая), определяющая её конкурентные преимущества. Основной домен может не приносить прибыль, но служить основой для её привлечения. Как например, поиск Google служит основой для рекламы – Google Ads.

🕚 Универсальный (generic). Деятельность, которую все компании выполняют или должны выполнять одинаково: бухгалтерский учёт, платежи, аутентификация, авторизация, шифрование, мониторинг и т.п. Сюда относятся готовые, широко используемые и проверенные временем решения. Например, для бухучёта 1С, для мониторинга OpenTelemetry и т.д.

🕚 Вспомогательный (supporting). Поддерживающий слой для основного поддомена. Это то поле, где универсальные решения не подходят или неудобны для ведения бизнеса. Например, административный интерфейс для настройки или поддержки системы.

2️⃣ Определим их свойства:

🕚 Основной – сложный и изменчивый, т.к. иначе не будет конкурентного преимущества.

🕚 Универсальный – сложный и стабильный, т.к. иначе каждый бы делал своё решение.

🕚 Вспомогательный – простой и стабильный, но со специфической бизнес-логикой, т.к. иначе подойдёт универсальное решение.

3️⃣ Выберем стратегию решения:

🕚 Основной – делаем самостоятельно, силами лучших кадров; используем передовые технологии и уникальные алгоритмы.

🕚 Универсальный – используем готовые, проверенные временем решения.

🕚 Вспомогательный – делаем самостоятельно, силами middle-специалистов.

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

И в контексте этого стоит сразу расставить красные флаги:

🚩 Если решением задачи вспомогательного поддомена является сложная машинерия, то, скорей всего, вы упоролись и делаете что-то, что решает уже имеющееся универсальное решение или их комбинация. Тут лучше остановиться, подумать и сделать шаг назад.

🚩 Сложность основного поддомена не определяет сложность решения. Напротив, сложность должна сводиться к минимуму, например, путем декомпозиции поддомена на более мелкие и делегирования большей части работы готовым решениям.

🚩 Ничего не стоит на месте. Типы поддоменов могут меняться, и это определяется деятельностью компании. Основной поддомен может стать вспомогательным для других; вспомогательный может трансформироваться в универсальное решение, которое начнёт приносить прибыль и станет основным для бизнеса; вспомогательные решения могут/должны заменяться универсальными.

Надеюсь, что этот простой приём поможет расходовать ресурсы более продуктивно. 🐈

#arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥2
Что лучше: поддомен или ограниченный контекст

Думаю, каждый слышал про домены и ограниченные контексты. Но как эти понятия соотносятся друг с другом? Это одно и тоже или нет? Когда использовать одно, а когда другое? Давайте сломаем стереотипы и развеем мифы! 🥷

На самом деле вопрос принципиальный, т.к. на сегодняшний день обе концепции являются доминирующими при проектировании распределённых систем и логическом делении системы на компоненты.

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


1️⃣ Суть контекста

▫️ Поддомены выявляются на основе анализа бизнеса.

▫️ Ограниченные контексты проектируются, исходя из требований и ограничений.

Это значит, что наряду с прочим поддомены задают дополнительный контекст, который должен учитываться при проектировании границ – ограниченных контекстов. Или, говоря проще, первое – это данность, второе – как мы эту данность учитываем при проектировании.

В нашем примере можно выделить два поддомена – основной и вспомогательный:
▫️ Игровой движок – распоряжается ресурсами с учётом механики игры.
▫️ Система учёта – регистрирует изменения ресурсов и строит аналитические срезы.


2️⃣ Ответственность контекста

Ограниченный контекст задаёт границы:

▫️ Единого языка
▫️ Модели решения
▫️ Физические границы (сервис, модуль, пакет)
▫️ Границы владения (исполнитель, команда)

Модель решения отражает ментальную модель экспертов предметной области на уровне, необходимом и достаточном для эффективного решения, следовательно, обеспечивает простоту понимания и реализации.

Разные модели одного и того же объекта определяют физические границы и, очевидно, границы владения: у контекста должен быть только один владелец.

Нарушение любой границы приведёт к хаосу и конфликтам.

В нашем примере эксперты игрового мира и учётной системы разговаривают на разном языке, но об одних и тех же вещах (ресурсах). Следовательно, есть предпосылки использовать два разных словаря и две разных модели. В игре будет использоваться модель "Транзакция" как событие действия над ресурсом, а в учётной системе модель "Проводка" как отражение учётной операции.


3️⃣ Размер контекста

Контекст может охватывать:

▫️ Один поддомен. Часто самый разумный выбор. Модели – раздельные, точные, прагматичные.

▫️ Часть поддомена. Если поддомен большой, его можно разделить на разные контексты, если это способствует эффективному решению разных задач.

▫️ Несколько поддоменов. Небольшие системы или монолиты. Модели – общие, большие, плохо поддерживаемые, провоцирующие конфликты.

Чем шире граница контекста, тем труднее поддерживать согласованность единого языка, терминологии, модели, инвариантов. Чем уже граница, тем больше издержки на интеграцию контекстов.

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


4️⃣ Взаимодействие контекстов

Сопряжение контекстов – это всегда интеграционный слой с определённым контрактом. Тема требует отдельного разбора, но тут важно ответить, на каком языке будет выражен контракт: в терминах модели какого контекста. Выбор всегда зависит от условий решения.

В нашем примере игра появилась раньше, и это основной поддомен, поэтому контракт взаимодействия будет выражен на языке игры. Игра будет оповещать учётную систему о свершении транзакций; учётная система будет преобразовывать транзакции в проводки.


➡️ Итог

Получается, что поддомены и ограниченные контексты совсем не конкурирующие, а ортогональные концепции. Тем не менее, они очень хорошо дополняют друг друга. Надеюсь, что теперь всё встало на свои места. 🐈

#arch #dev
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥3👍1🤩1