<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Архитектура ПО</title>
    <description/>
    <link>https://tproger.ru/tag/software-architecture</link>
    <atom:link href="https://tproger.ru/tag/software-architecture/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Fri, 23 Oct 2026 20:58:53 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Архитектура ПО</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>Монолит, модульный монолит или микросервисы: как выбрать архитектуру</title>
      <link>https://tproger.ru/articles/monolit-modulnyj-monolit-ili-mikroservisy-kak-vybrat-arhitek</link>
      <comments>https://tproger.ru/articles/monolit-modulnyj-monolit-ili-mikroservisy-kak-vybrat-arhitek?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/monolit-modulnyj-monolit-ili-mikroservisy-kak-vybrat-arhitek</guid>
      <description><![CDATA[<p>Модульный монолит развёртывается целиком, а сервисы можно обновлять отдельно. Как выбрать архитектуру с учётом нагрузки, транзакций и ресурсов команды?</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/monolit-modulnyj-monolit-ili-mikroservisy-kak-vybrat-arhitek">Монолит, модульный монолит или микросервисы: как выбрать архитектуру</a>»</p>]]></description>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 28 Sep 2026 11:44:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда пользователей становится больше, приложение может начать тормозить, а с добавлением новых функций код усложняется: перед каждым обновлением приходится проверять, как изменения повлияют на связанные части. В зависимости от причины можно добавить серверные мощности, разделить код на модули с понятными задачами или выделить самостоятельные сервисы, которые можно обновлять отдельно.</p><h2>Чем отличаются монолит, модульный монолит и микросервисы</h2><p>Представим приложение магазина. В нём есть заказы, каталог товаров и биллинг, который рассчитывает скидки и выставляет счета. Эти части связаны бизнес-логикой: для персональной скидки нужна сумма прошлых заказов, а при покупке нужно знать, есть ли товар на складе.</p><p>В <b>монолите</b> эта функциональность входит в одно приложение. Его части могут вызывать функции друг друга внутри процесса и обращаться к общей базе данных. Новую версию собирают и развёртывают целиком, даже если изменения касаются только расчёта скидки.</p><p>Такое устройство удобно на старте. Команда работает с общим приложением, и для проверки взаимодействия заказов с биллингом достаточно запустить его вместе с необходимой инфраструктурой. Обновление связанных частей можно включить в один релиз. Разработчикам проще проследить выполнение операции, когда её логика находится рядом.</p><p>По мере роста проекта становится важнее то, как устроен код внутри приложения. Если расчёт скидок, обработка заказов и работа с товарами связаны, изменение одной функции может затронуть соседние. Разработчику приходится учитывать всё больше связей, а проверка последствий занимает больше времени.</p><p><b>Модульный монолит</b> сохраняет общее развёртывание, но внутри разделён на модули с обязанностями и границами. Модуль заказов отвечает за заказы, биллинг содержит правила расчётов. Взаимодействие между ними организуют через определённые интерфейсы: по ним должно быть понятно, какие данные запрашивает соседняя часть и какой результат получает. Поэтому, когда меняются правила расчёта скидок, разработчик знает, где искать соответствующий код и какие части приложения используют результат. При этом вызов между модулями может оставаться обычным вызовом функции в памяти.</p><p><b>Модульный монолит</b> относится к монолитной архитектуре, из его названия вы можете понять организацию внутренних границ. Она требует работы с зависимостями в коде: раскладывание файлов по папкам само по себе ещё ничего не говорит о том, насколько самостоятельно устроены модули.</p><p>В <b>микросервисной архитектуре</b> части системы работают как самостоятельные сервисы. Заказы и биллинг можно развёртывать отдельно, выделять им разные ресурсы и разрабатывать силами разных команд. Для получения суммы заказов биллинг обращается к сервису заказов через API, получает ответ и использует его в расчёте.</p><p>Здесь появляется возможность выпускать изменения одного сервиса независимо от остальных, пока сохраняется совместимость взаимодействия. Вместе с ней возникает ответственность за контракт: сервис заказов должен возвращать данные в том виде, который понимает биллинг. Если изменение затрагивает обе стороны, командам нужно согласовать обновление.</p><h2>Что меняется, когда вызов функции становится сетевым запросом</h2><p>Вернёмся к персональной скидке. Биллинг должен узнать, на какую сумму пользователь сделал заказы за год. Пока логика находится в одном приложении, он может получить результат через внутренний вызов. В варианте с общей БД нужные данные доступны через запрос к ней.</p><p>После выделения заказов в сервис путь становится длиннее. Биллинг отправляет запрос к его API. Сервис заказов обращается к своей базе, формирует ответ и передаёт его обратно. Затем биллинг разбирает полученные данные и рассчитывает скидку.</p><p>На каждом таком переходе появляется дополнительная работа. Данные нужно подготовить к передаче и восстановить на другой стороне, дождаться ответа по сети, обработать результат. Эти расходы могут быть приемлемыми для одной операции. Если пользовательский запрос запускает длинную цепочку обращений, время ожидания начинает зависеть от всех её участников.</p><p>Например, страница профиля может собирать данные из нескольких сервисов. Тогда разработчику приходится выяснять, на каком участке задерживается ответ. Медленная работа одного сервиса способна задержать весь сценарий, который от него зависит. Физическое разделение компонентов сохраняет логические связи между ними.</p><p>Контракт API тоже становится частью работы. Если сервис меняет формат ответа, нужно учесть потребителей этих данных. Изменение, которое внутри монолита могло попасть в один релиз, в распределённой системе может потребовать обновления нескольких сервисов в согласованном порядке. При частых совместных изменениях ожидаемая независимость релизов сокращается.</p><p>Кроме кода прикладных задач, команда поддерживает инфраструктуру каждого сервиса. Нужно организовать сборку и развёртывание, наблюдать за работой компонентов, собирать их логи. При отладке приходится восстанавливать ход операции по нескольким частям системы. Для локальной разработки также нужно продумать, какие сервисы и базы потребуются, чтобы проверить изменение.</p><p>Стоимость взаимодействия покажем на примере системы мониторинга качества видео Amazon Prime Video. Сначала она работала как распределённое решение на AWS Lambda и Step Functions. При увеличении нагрузки обмен данными и управление выполнением компонентов потребовали слишком больших расходов. Команда объединила систему в монолитное приложение, после чего расходы на неё снизились примерно на 90%.</p><p>Результат относится к конкретной системе мониторинга с её характером вычислений и обмена данными. Для выбора архитектуры здесь полезно проверить, какую долю работы составляют полезные вычисления, а какую занимает координация компонентов. Чем чаще элементы приложения обращаются друг к другу, тем внимательнее нужно оценивать стоимость их разделения.</p><h2>Как масштабировать монолит и микросервисы</h2><p>Монолитное приложение можно запускать в нескольких экземплярах. Перед ними размещают балансировщик, который распределяет входящие запросы. Каждый экземпляр выполняет код того же приложения, поэтому монолитная архитектура совместима с горизонтальным масштабированием: нагрузку распределяют между дополнительными экземплярами.</p><p>С микросервисами применяется похожий подход. Если сервис заказов получает больше запросов, запускают дополнительные экземпляры именно этого сервиса. Биллинг при этом может продолжать работать с прежними ресурсами. Такая избирательность полезна, когда нагрузка на части системы заметно различается.</p><p>Однако увеличение числа обработчиков помогает в пределах возможностей остальных компонентов. Если все экземпляры сервиса заказов обращаются к одной перегруженной БД, она продолжит ограничивать обработку запросов. Поэтому перед изменением архитектуры нужно разобраться, где именно приложение упирается в доступные ресурсы.</p><h3>Воркеры и очереди задач</h3><p>Часть работы можно выполнять в фоне. Пользователь получает данные, необходимые для текущего действия, а отложенные вычисления выполняются отдельно. Для этого используют воркеры: процессы, которые забирают задачи из очереди и обрабатывают их.</p><p>В нашем магазине таким вычислением может быть расчёт персональной скидки по прошлым заказам. Воркер получает задачу, считает результат и сохраняет его в БД. Приложение затем использует подготовленное значение. Возможность вынести конкретный расчёт в фон зависит от того, в какой момент его результат нужен пользователю.</p><p>У монолита воркеры могут использовать ту же кодовую базу, что и веб-приложение, но выполнять разные группы задач. Один занимается расчётами биллинга, другой обрабатывает задачи, связанные с товарами. Для каждого направления можно организовать свою очередь и подобрать число исполнителей.</p><p>Допустим, в очереди четыре задачи, а свободных воркеров три. Каждый возьмёт по одной задаче, оставшаяся дождётся первого освободившегося исполнителя. Если очередь регулярно растёт и вычисления позволяют распределить работу, можно увеличить число воркеров. Для этого достаточно масштабировать обработку фоновых задач в существующей архитектуре.</p><p>Ресурсы воркеров тоже могут различаться. Расчётам, которым нужно держать в памяти большой объём данных, выделяют больше памяти. Более лёгким задачам достаточно меньших ресурсов. Такой подбор применим и к монолиту, и к отдельным сервисам.</p><h3>Чтение и запись в базе данных</h3><p>Для распределения нагрузки на чтение можно использовать реплики БД. Основная база принимает изменения, а её копии получают эти изменения через механизм репликации. Часть запросов на чтение направляют к копиям, разгружая основную базу.</p><p>В этой схеме запись по-прежнему сосредоточена в основной БД. Если ограничение связано именно с ней, увеличение числа реплик для чтения решает другую задачу. Потребуется отдельно рассматривать, как устроены данные и операции записи.</p><p>Разделение по сервисам позволяет распределить запись между разными базами, когда данные принадлежат разным частям системы. При этом сильно нагруженная база конкретного сервиса всё равно может потребовать масштабирования. Оценивать нужно каждый участок, на котором сосредоточена работа.</p><p>Возможен и шардинг: распределение данных между несколькими базами, например по пользователям или клиентам. Такой подход можно применять в разных архитектурах. Он требует продумать, как приложение найдёт нужные данные и как будет работать с информацией, относящейся сразу к нескольким группам.</p><h2>Что усложняется при разделении данных между сервисами</h2><p>Заказы связаны с товарами независимо от того, где выполняется код. Если обе сущности хранятся в общей реляционной БД, данные о них можно соединить запросом с JOIN. База выполняет связанную операцию, а приложение получает результат.</p><p>Когда каждая часть системы управляет своими данными и предоставляет их через API, получение общей информации требует обращений к соответствующим сервисам. Результаты нужно собрать и обработать. В системе появляется код, который организует эти обращения и учитывает доступность участников.</p><p>Поэтому перед выделением компонента полезно проследить, какие сведения нужны ему для обычной работы. Если почти каждый расчёт биллинга требует обращения к заказам, а затем к товарам, эти зависимости войдут в стоимость нового сервиса. Само разделение сохранит потребность в данных соседних частей.</p><p>Более сложный вопрос возникает, когда операция меняет данные сразу в нескольких местах. Допустим, покупатель оформляет заказ, а система должна зарезервировать товар на складе. Запись о заказе и доступный остаток должны соответствовать друг другу. Если товар уже закончился, приложение должно корректно обработать эту ситуацию.</p><p>При выполнении связанных изменений внутри одной БД можно использовать общую транзакцию. Она позволяет применить предусмотренные изменения вместе или отменить их при неудаче. Разработчик описывает операцию с учётом возможностей и правил работы выбранной базы.</p><p>После разделения данных между сервисами согласование становится отдельной задачей. Один участник может успеть выполнить свою часть, пока другой столкнулся с ошибкой. Команде нужно определить, что произойдёт с уже внесёнными изменениями и какой итог получит пользователь.</p><p>Один из подходов к такому согласованию называется Saga. Общую операцию описывают как последовательность шагов с действиями на случай неудачи. Для заказа и резервирования потребуется продумать, как система поступит, если выполнить весь сценарий не удалось. Эти правила становятся частью прикладной логики и требуют отдельной проверки.</p><p>Здесь возрастает объём работы по обеспечению согласованного результата. Чем больше участников и связей между ними, тем больше сценариев должна учитывать команда. Поэтому частые операции, которые тесно связывают данные разных частей приложения, влияют на выбор границ сервисов.</p><h2>По каким признакам выбирать архитектуру для проекта</h2><p>Начать стоит с устройства разработки. Небольшой команде, которая проверяет продуктовую идею и часто меняет связанную функциональность, удобно работать с общим приложением. Один релиз позволяет одновременно обновить нужные части, а разработчики тратят меньше времени на инфраструктуру взаимодействия.</p><p>Для такой ситуации монолит даёт простой старт. По мере того как проясняются обязанности частей приложения, их можно оформлять в модули. Это помогает ограничивать зависимости в коде и сохранять понятную структуру, пока общее развёртывание подходит команде.</p><p>Модульный монолит особенно полезен, когда функциональность уже выросла, а многие изменения по-прежнему удобно выпускать вместе. У разработчиков есть границы, по которым можно разделять работу внутри приложения. При этом сохраняется возможность выполнять внутренние вызовы и работать с тесно связанными данными в общей системе.</p><p>В большой организации ситуация может быть другой. Несколько команд развивают самостоятельные направления и хотят выпускать обновления в собственном темпе. Если границы этих направлений совпадают с границами функциональности, отдельные сервисы позволяют согласовать устройство системы с организацией разработки.</p><p>Следующий критерий связан с нагрузкой. Например, обработка изображений может требовать заметно больше ресурсов, чем остальная часть приложения. Выделение такой функциональности позволяет масштабировать её отдельно. Перед этим стоит сравнить пользу самостоятельного сервиса с возможностями фоновых воркеров в текущем приложении.</p><p>Также нужно учитывать бюджет эксплуатации. Каждый дополнительный сервис требует внимания к развёртыванию и наблюдению за его работой. Если поддержкой всей инфраструктуры занимается та же небольшая команда, время на неё придётся учитывать в сроках разработки функций продукта.</p><p>Потребность в разных технологиях может стать ещё одним аргументом за разделение. Для отдельной задачи бывает удобнее другой язык или стек. Тогда сервис даёт возможность использовать его локально. Вместе с этим потребуется поддерживать соответствующие знания в команде и учитывать особенности новой технологии при эксплуатации.</p><p>Наконец, проверьте требования к данным и времени ответа. Если пользовательский сценарий требует частых синхронных обращений между частями системы, сетевое взаимодействие войдёт в его стоимость. Если операция меняет тесно связанные данные, нужно заранее оценить сложность согласования этих изменений после разделения.</p><p>Чтобы обсудить решение на конкретных примерах, команда может ответить на несколько вопросов:</p><ul><li>Какие части приложения обычно меняются вместе и почему им нужен общий релиз?</li><li>Какая функциональность требует самостоятельного масштабирования и где именно возникает нагрузка?</li><li>Какие данные потребуются выделенному сервису для выполнения обычной операции?</li><li>Кто будет отвечать за его развёртывание и разбирать проблемы в эксплуатации?</li><li>Можно ли разрабатывать и проверять этот сервис, запуская только необходимые ему компоненты?</li></ul><p>Ответы позволяют увидеть цену самостоятельности. Если выделение сервиса освобождает команду от общего графика релизов и даёт полезное управление ресурсами, эти преимущества можно сопоставить с расходами на поддержку. Если же каждое изменение продолжает затрагивать несколько сервисов, сначала стоит пересмотреть их границы и зависимости.</p><h2>Как понять, что компонент готов стать отдельным сервисом</h2><p>Архитектуру можно менять постепенно. Можно начать с монолита, оформить внутри него модули, а затем при необходимости выделить часть функциональности в сервис. К этому моменту команда будет знать, какую нагрузку получает компонент и с какими частями приложения он связан.</p><p>Для выделения нужен компонент с понятной самостоятельной обязанностью. Его должно быть возможно описать через выполняемую работу и необходимые данные. Полезно заранее проследить основные сценарии: какие запросы приходят на вход, откуда берётся информация для их обработки и кому потребуется результат.</p><p>Возьмём биллинг. Если он при каждом действии запрашивает множество сведений о заказах и товарах, после выделения эти обращения придётся поддерживать через интерфейсы сервисов. Стоит оценить их частоту и понять, насколько биллинг сможет развиваться самостоятельно. Если изменение расчёта каждый раз требует одновременно переделывать соседние части, независимый релиз останется ограничен их зависимостями.</p><p>Следующая проверка касается локальной разработки. Сервис проще сопровождать, когда разработчик может запустить его, выполнить нужную проверку и разобраться в результате без развёртывания всего проекта. Чем больше других сервисов и баз приходится поднимать ради небольшой доработки, тем внимательнее стоит рассмотреть выбранные границы.</p><p>Затем нужно сопоставить ожидаемую пользу с работой по переносу. У компонента появится собственное развёртывание, его взаимодействие с остальным приложением потребуется организовать и проверить. Если выделяются данные, отдельно планируют согласование операций, которые раньше выполнялись внутри одной БД.</p><p>Для существующего проекта такие изменения входят в более широкую работу по модернизации ПО. Centicore Group занимается обновлением легаси-кода, баз данных и инфраструктуры; в услугу входят рефакторинг и обновление технологического стека. Если команда планирует переработку работающей системы, можно<a href="https://centicore.ru/services/legacy-modernization/"> обсудить её модернизацию с Centicore Group</a>.</p><p>После выделения компонента у команды должна появиться конкретная возможность: выпускать его изменения в собственном темпе или подбирать ему ресурсы отдельно от остального приложения. Эту возможность полезно сформулировать ещё до переноса, тогда результат можно оценивать по исходной задаче, а дальнейшие изменения архитектуры планировать по мере появления новых потребностей.</p><h2>Итого</h2><p>Монолит упрощает совместную разработку и выпуск связанных изменений. Модульная организация помогает сохранять понятные границы внутри такого приложения. Микросервисы дают больше самостоятельности отдельным частям и командам, одновременно увеличивая работу по их взаимодействию.</p><p>Перед выделением сервиса сформулируйте, какая конкретная задача требует самостоятельного развёртывания или отдельного управления ресурсами. Затем проследите его зависимости и операции с данными. Если команда понимает, какую пользу получит и как будет поддерживать новые связи, у неё есть основание для разделения. При проблемах с нагрузкой на конкретный участок сначала оцените доступные способы масштабирования этого участка в текущей архитектуре.</p>]]></content:encoded>
    </item>
    <item>
      <title>Идемпотентность и Outbox: как не выполнить одну операцию дважды</title>
      <link>https://tproger.ru/articles/idempotentnost-i-outbox-kak-ne-vypolnit-odnu-operaciyu-dvazhdy</link>
      <comments>https://tproger.ru/articles/idempotentnost-i-outbox-kak-ne-vypolnit-odnu-operaciyu-dvazhdy?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/idempotentnost-i-outbox-kak-ne-vypolnit-odnu-operaciyu-dvazhdy</guid>
      <description><![CDATA[<p>Как сделать ручку идемпотентной, где ломается наивная проверка ключа и зачем нужен transactional outbox. Забирайте код на Python, SQL и Node.js и чеклист.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/idempotentnost-i-outbox-kak-ne-vypolnit-odnu-operaciyu-dvazhdy">Идемпотентность и Outbox: как не выполнить одну операцию дважды</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 09 Sep 2026 05:00:35 GMT</pubDate>
      <content:encoded><![CDATA[<p>История, с которой начинает автор одного из разборов ниже, звучит буднично. Платёжный шлюз не ответил вовремя, клиентская библиотека повторила запрос, и покупателя списали дважды. Сумма небольшая, а возврат, тикет в поддержку и восстановление доверия заняли недели.</p><p>Виноват тут не шлюз. Виноват сервер, который исходил из того, что каждый запрос приходит ровно один раз. В сети, где теряются ответы, это допущение неверно всегда.</p><p>Идемпотентность — свойство операции, при котором повторное выполнение приводит к тому же состоянию, что и однократное. Сам ответ при этом может отличаться: повторный DELETE вернёт 404 вместо 200, и метод от этого идемпотентным быть не перестаёт. У HTTP это закреплено на уровне методов: <a href="https://www.rfc-editor.org/rfc/rfc9110">RFC 9110</a> относит к идемпотентным PUT, DELETE и безопасные методы, а POST идемпотентным по своей природе не является. Бизнес-операции почти всегда отправляют именно через POST.</p><p>Клиент не может отличить «сервер не получил запрос» от «сервер выполнил операцию, но ответ потерялся», поэтому повтор придёт в любом случае.</p><p>Ключ идемпотентности генерирует клиент, а сервер хранит связку ключа со слепком запроса и с готовым ответом, чтобы повтор получил тот же результат, а не новый.</p><p>Проверка «посмотреть и вставить» неатомарна: два одновременных повтора оба увидят отсутствие ключа. Нужен уникальный индекс или условная запись.</p><p>Запись в базу и отправка в очередь в одну транзакцию не помещаются. Обёртывать сетевой вызов в BEGIN и COMMIT вредно вдвойне: корректности не даёт, а пул соединений выедает.</p><p>Transactional outbox решает это одной таблицей: событие пишется в той же транзакции, что и бизнес-запись, а отдельный процесс доставляет его в очередь.</p><h2>Почему повтор неизбежен</h2><p>Сеть отказывает так, что установить факт доставки невозможно в принципе. Есть три типовых сценария, и внешне они неразличимы:</p><ul><li>Сервер обработал запрос, но ответ потерялся по дороге назад.</li><li>Сервер всё ещё считает, а у клиента уже сработал таймаут.</li><li>Балансировщик повторил запрос сам, никого об этом не уведомив.</li></ul><p>Для GET повтор безвреден. Для POST /payments это второе списание. Отсюда и правило: одна логическая операция должна приводить к одному записанному итоговому состоянию, сколько бы раз её ни отправили. Осознанно новая операция обязана прийти с новым ключом.</p><h2>Два способа сделать ручку идемпотентной и один в довесок</h2><h3>Способ первый: ключ идемпотентности</h3><p>Самый распространённый вариант: клиент генерирует уникальный идентификатор на одну логическую операцию и повторяет его при каждой попытке. Сервер хранит соответствие ключа и ответа, а на дубликате возвращает сохранённый результат вместо повторного выполнения работы.</p><p>Три детали, на которых ошибаются чаще всего. Ключ генерирует клиент, а не сервер: смысл в том, что сервер сам по себе не отличит новый запрос от повтора. Ключ должен быть с высокой энтропией, обычно это UUID v4; выводить его из изменяемого или малоразнообразного поля вроде номера клиента нельзя, потому что это повышает риск коллизии и переигрывания чужой операции.</p><p>И последнее: повтор должен получить <b>тот же самый ответ</b>. Если первая попытка вернула 201 с идентификатором платежа, то и повтор возвращает те же 201 и тот же идентификатор, а не 409 и не пустую двухсотку. Иначе клиент решит, что операция не прошла, и попробует ещё раз.</p><h3>Способ второй: сделать операцию идемпотентной по смыслу</h3><p>Иногда ключ не нужен вовсе, потому что семантику можно спроектировать идемпотентной с самого начала. Классический пример — PUT /users/42, который задаёт полное представление ресурса: отправьте его дважды, и состояние будет одним и тем же.</p><p>Для операций создания трюк в том, чтобы выводить идентификатор из самого запроса, а не генерировать случайный на каждый вызов:</p><p>Идентификатор детерминированно выводится из почты, поэтому повторный запрос даёт того же пользователя без дублирующей строки. Плата за простоту — необходимость естественного уникального ключа: почты, артикула, внешнего идентификатора. Хеш при этом считается от точной строки, поэтому почту нужно привести к нижнему регистру и обрезать пробелы до хеширования: иначе один и тот же ящик с заглавной буквы даст второго пользователя. И ключ должен быть неизменным: если пользователь сменит почту, выведенный из неё идентификатор либо останется прежним и перестанет соответствовать данным, либо изменится и оторвётся от всех ссылок на него в соседних таблицах. Нет подходящего ключа, возвращайтесь к первому способу.</p><h3>Довесок: оптимистичная блокировка</h3><p>Этот приём идемпотентности не даёт и в списке стоит по другой причине. Ключ защищает от повторов одного и того же запроса, но ничего не делает с двумя <b>разными</b> запросами, которые правят одну запись. Здесь работает проверка версии: клиент присылает версию, которую видел, а сервер отклоняет обновление при несовпадении.</p><p>Обратите внимание, что проверка версии живёт внутри самого UPDATE. Разнести её на отдельное чтение и последующую запись означало бы воспроизвести ровно ту гонку, о которой пойдёт речь в следующем разделе: два запроса с одной устаревшей версией оба прошли бы проверку и оба записали бы результат. Идемпотентным одиночный запрос этот приём не делает, зато закрывает частый источник задвоенных эффектов: двух писателей, затирающих работу друг друга.</p><h2>Где наивная реализация ключа разваливается</h2><h3>Гонка в проверке</h3><p>Последовательность «проверить наличие ключа, потом вставить» содержит зазор, в который помещаются оба одновременных повтора: обе стороны видят, что ключа нет, и обе выполняют работу. Резервировать ключ нужно атомарно, через уникальное ограничение базы, условную запись или транзакционный compare-and-set (атомарное сравнение с записью).</p><p>Это же относится и к примеру с платежом выше: замена словаря в памяти на Redis гонку не закрывает, пока проверка и вставка остаются двумя отдельными командами. Нужен атомарный примитив резервирования, вроде SET key value NX или INSERT ... ON CONFLICT DO NOTHING.</p><p>Проверяется это только настоящей конкуренцией. Юнит-тест, вызывающий функцию дважды подряд, гонку не поймает никогда. Отправьте полсотни одновременных запросов с одним ключом и убедитесь, что побочный эффект произошёл ровно один раз.</p><h3>Слепок запроса, а не только ключ</h3><p>Хранить один ключ недостаточно. Сервер канонизирует поля, определяющие бизнес-смысл операции, и считает от них хеш. Канонизация означает приведение к единому виду порядка полей, форматов чисел и дат, опущенных значений по умолчанию и незначащих пробелов. Если повторный вызов обязан воспроизвести решение, принятое по прежним правилам, в слепок включают ещё и версию политики. Пример: между первой попыткой и повтором поменялись тарифы, и повтор обязан вернуть старую цену, а не пересчитать по новой. Без версии в слепке сервер этого различия не увидит.</p><p>Когда тот же ключ приходит с другим слепком, это ошибка на стороне клиента, и отдавать ему старый результат нельзя. <a href="https://dev.to/seo_optimization_591fad6c/designing-idempotent-decision-endpoints-that-survive-real-retries-6c1">Разбор проектирования идемпотентных ручек</a> предлагает отвечать 409 Conflict; <a href="https://dev.to/sirmax/3-ways-to-make-your-api-requests-idempotent-with-working-code-4inl">автор практического руководства</a> в этом случае возвращает 422. Важно, чтобы выбранный код был задокументирован и никогда не подменяется молчаливой отдачей чужого ответа.</p><h3>Состояния и коды ответа</h3><p>Минимальная модель состояний записи выглядит так: PROCESSING, SUCCEEDED, FAILED_RETRYABLE и FAILED_FINAL. Рядом хранятся ключ операции, слепок, отметки времени, идентификатор решения, снимок ответа и версия политики. Клиенту нужна детерминированная карта из состояния в код ответа:</p><ul><li>201 или 200 — первый успешно завершённый результат.</li><li>200 с явной пометкой о повторе — проигрывание сохранённого ответа.</li><li>202 со ссылкой на статус — работу уже выполняет другой обработчик, начинать вторую не нужно.</li><li>409 — ключ переиспользован с другим содержимым.</li><li>Задокументированная финальная ошибка — обработка провалилась, и автоматическое продолжение небезопасно.</li></ul><p><b>Срок жизни ключей:</b><br />Хранить их вечно не нужно, это медленная утечка. Сутки покрывают практически любое реальное окно повторов, но выбирать срок стоит от риска предметной области: у платежей и у рекомендаций он разный. И помните, что ключ приходит снаружи: ограничьте длину и набор символов, привяжите его к арендатору и не дайте одному пользователю вытащить результат чужой операции.</p><h2>Вторая половина задачи: база и очередь</h2><p>Допустим, ручка стала идемпотентной. Остаётся более коварная проблема: почти всякая бизнес-операция пишет не в одно место. Заказ сохраняется в базу и публикует событие в очередь, чтобы склад начал сборку, почтовый сервис отправил подтверждение, а антифрод посмотрел на транзакцию.</p><p>Обе половины статьи растут из одного факта: ни HTTP, ни очередь не обещают доставку ровно один раз, они обещают её хотя бы один раз. Поэтому защищаться приходится дважды, на входе и на выходе.</p><p>Это две записи в две разные системы, и общей транзакции у них нет. Упал процесс, моргнула сеть, выкатился деплой между двумя вызовами — одна сторона зафиксирована, вторая нет. Заказ подтверждён на экране покупателя, а склад о нём не знает. Ошибка при этом нигде не залогирована и алерт не сработал.</p><h3>Почему обернуть это в транзакцию нельзя</h3><p>Соблазнительный и заведомо неверный вариант выглядит так:</p><p>Транзакция базы не имеет власти над очередью и умеет откатывать только операции базы. Если отправка прошла, а COMMIT упал, сообщение уже в очереди и забрать его оттуда нельзя.</p><p>Есть и вторая беда, чисто эксплуатационная. Такой код держит открытое соединение и блокировки строк всё время сетевого вызова. Обычно очередь отвечает быстро, но под нагрузкой, ретраями или деградацией вызов растягивается на секунды, и все запросы к тем же строкам стоят в очереди. Это надёжный способ исчерпать пул соединений и уронить заодно ни в чём не повинные части сервиса.</p><h3>Outbox: событие как строка в той же транзакции</h3><p>Идея паттерна в том, чтобы перестать считать публикацию второй записью. Вместо вызова очереди приложение вставляет строку в таблицу outbox в той же транзакции, что и бизнес-запись. Отдельный процесс читает таблицу и публикует сообщения дальше.</p><p>Откатилась транзакция, и вместе с ней исчезла строка outbox: осиротевшего сообщения в очереди не осталось. Упало приложение сразу после фиксации — строка на месте со статусом pending, и доставщик заберёт её на следующем проходе. Паттерн опирается ровно на одну гарантию, которую база и так даёт: атомарность одной транзакции.</p><p>Доставщик выбирает пачку необработанных строк и помечает их отправленными только после подтверждения очередью. Ключевая деталь здесь одна:</p><p>Этот SELECT и последующая простановка статуса выполняются в одной транзакции: блокировка живёт до фиксации. Благодаря SKIP LOCKED доставщик масштабируется горизонтально из коробки, каждый экземпляр берёт свой набор строк. А если он упадёт посреди пачки, вся пачка откатится и уйдёт повторно, что даёт ещё один довод в пользу идемпотентного потребителя.</p><p>Может показаться, что здесь мы делаем ровно то, что осудили выше: держим блокировку на время сетевого вызова. Разница в том, что блокируется не бизнес-таблица и соединение берётся не из пула, обслуживающего пользовательские запросы, а SKIP LOCKED не даёт одной медленной строке задержать остальные.</p><h3>Потребитель обязан быть идемпотентным</h3><p>Если доставщик упал посреди прохода, на следующем он переотправит те же строки. Очереди вроде SQS и Kafka в типовой конфигурации гарантируют доставку «хотя бы один раз», поэтому дедупликация на приёмной стороне обязательна, и уникальным ключом служит идентификатор события или решения.</p><p>Делает это условная запись. Важная деталь, на которой легко ошибиться: сама по себе она пустой операцией не становится. При несовпадении условия драйвер бросает исключение, и его нужно поймать явно, отличив штатный повтор от настоящей ошибки:</p><p>Настройки продюсера и подтверждения брокера снижают количество повторов, но не снимают с приложения ответственность за однократность бизнес-эффекта.</p><h3>Что добавить перед продакшеном</h3><p>Двух статусов мало. Нужен статус failed и счётчик попыток: после N неудач строка помечается провалившейся и перестаёт крутиться в цикле вечно. На стороне очереди настраивается очередь недоставленных сообщений, чтобы то, что потребитель не смог обработать, оседало в наблюдаемом месте, а не исчезало молча.</p><p>Когда задержка опроса становится критичной, полагающийся на периодический опрос доставщик заменяют захватом изменений: инструменты вроде Debezium читают журнал предзаписи PostgreSQL и публикуют изменения без паузы на опрос. Таблица outbox и потребитель при этом не меняются. Но это заметно более тяжёлое эксплуатационное обязательство, поэтому начинать почти всегда стоит с опроса.</p><h2>Что забрать с собой</h2><p>Обе части задачи выглядят избыточными ровно до первого инцидента. Тридцать строк кода с ключом идемпотентности стоят дешевле одного тикета с заголовком «вы списали дважды», а таблица outbox дешевле расследования, почему заказ есть у клиента и отсутствует на складе.</p><p>Коварство проблемы двух записей в том, что наивная реализация работает правильно почти всегда. Она отказывает в зазоре между двумя системами, и зазор этот становится виден только когда что-то пошло не так в самый неподходящий момент. К моменту, когда расхождение заметят в продакшене, данные уже разъехались, и красивого способа их починить не будет.</p><p>Как одно неверное допущение о порядке вызовов обернулось эпидемией задвоенных операций, показано в <a href="https://tproger.ru/articles/skrytyj-sboj-idempotentnosti-v-finteh-sisteme-razbor-incidenta">разборе реального инцидента в финтех-системе</a>.</p><p>Материалы, на которых основан разбор: <a href="https://dev.to/sirmax/3-ways-to-make-your-api-requests-idempotent-with-working-code-4inl">три паттерна идемпотентности с рабочим кодом</a>, <a href="https://dev.to/seo_optimization_591fad6c/designing-idempotent-decision-endpoints-that-survive-real-retries-6c1">проектирование ручек, переживающих реальные повторы</a> и <a href="https://www.freecodecamp.org/news/how-to-fix-the-dual-write-problem-in-node-js-with-the-outbox-pattern/">пошаговая сборка outbox на Node.js</a>.</p><p>Откройте самый денежный обработчик в своём сервисе и попробуйте отправить в него один и тот же запрос дважды. Если во второй раз что-то произошло, вы уже знаете, с чего начать понедельник.</p>]]></content:encoded>
    </item>
    <item>
      <title>Обвязка важнее модели: три слоя, которые определяют результат</title>
      <link>https://tproger.ru/articles/obvyazka-vazhnee-modeli-tri-sloya-kotorye-opredelyayut-rezultat</link>
      <comments>https://tproger.ru/articles/obvyazka-vazhnee-modeli-tri-sloya-kotorye-opredelyayut-rezultat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/obvyazka-vazhnee-modeli-tri-sloya-kotorye-opredelyayut-rezultat</guid>
      <description><![CDATA[<p>Почему код вокруг модели влияет на результат сильнее выбора модели: маршрутизация вычислений, автоматические проверки отказов и контроль границ информации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/obvyazka-vazhnee-modeli-tri-sloya-kotorye-opredelyayut-rezultat">Обвязка важнее модели: три слоя, которые определяют результат</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Информационная безопасность]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Промпты]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Sep 2026 13:00:17 GMT</pubDate>
      <content:encoded><![CDATA[<p>Спор о том, какая модель лучше, съедает почти всё внимание. Между тем на поведение системы куда сильнее влияет то, что построено вокруг модели: как формируются запросы, что попадает в контекст, какие проверки стоят на выходе и кому позволено читать сохранённые данные.</p><p>Всё это принято называть обвязкой, и определение у неё простое.</p><blockquote>Обвязка — это весь код между тем, что вы печатаете, тем, что получает модель, тем, что модель выдаёт, и тем, что вы видите. И это много кода, не имеющего отношения к ИИ. Модели при этом выглядят в значительной степени взаимозаменяемыми.</blockquote><p>Разбираем три слоя обвязки, на которых результат меняется заметнее, чем от смены модели: маршрутизация вычислений, страховка надёжности в конвейере и контроль границ информации.</p><p>Обвязка — это код без единой нейросети внутри: сборка запроса, управление контекстом, проверки на выходе и координация нескольких моделей.</p><p>Маршрутизация задач между дорогой внешней моделью и дешёвой локальной даёт выигрыш в скорости, приватности и цене без смены качества на основных сценариях.</p><p>Анализ сгенерированного кода не показывает, как он поведёт себя при отказе зависимости, поэтому проверки надёжности строятся на воспроизведении реальных отказов, а не на чтении текста.</p><p>Пять источников почти всех сбоев одни и те же: процессор, память, диск, ввод-вывод и сеть. С них и начинают автоматические проверки.</p><p>Модели с постоянной памятью раскрывают лишние данные в неподходящем контексте, причём доля нарушений растёт с числом задач, а просьба быть осторожнее проблему не решает.</p><h2>Слой первый: что и где считать</h2><p>Первое, что делает обвязка, — решает, какой моделью обрабатывать конкретную задачу. Шнайер прямо советует руководителям по безопасности <a href="https://www.schneier.com/news/archives/2026/07/why-an-ai-harness-may-matter-more-than-the-latest-model.html">смотреть мимо брендов моделей и разбираться с окружающим их программным слоем</a>: он управляет промптами, выводом, контекстом, ограничениями и координацией нескольких моделей одновременно.</p><p>Отдельная функция этого слоя — маршрутизация между дорогими внешними моделями и дешёвыми локальными. По мере того как компактные системы приближаются по качеству к передовым, всё больше задач имеет смысл считать локально: это быстрее, дешевле, приватнее и оставляет контроль на своей стороне.</p><p>Из того же наблюдения следует острый вывод, который Шнайер делает применительно к безопасности: раз возможности моделей широко доступны и во многом взаимозаменяемы, экспортные ограничения и закрытие доступа их не сдержат. Реальные риски он видит в другом: в концентрации корпоративной власти, в слабой целостности моделей и в плохо устроенных стимулах. Для инженера это переводится в практическое требование — проверять заявления поставщика и не полагаться на то, что выбор конкретного вендора сам по себе что-то гарантирует.</p><h2>Слой второй: страховка надёжности в конвейере</h2><p>Оговорка сразу: это уже не обвязка вокруг обращения к модели, а обвязка процесса поставки. Принцип тот же, объект другой: проверяется не ответ модели, а код, который агент написал и который вот-вот уедет в продакшен.</p><p>Код теперь пишется быстрее, и это меняет характер рисков, а не только их количество. Опечаток в сгенерированном коде стало меньше, зато прибавилось незапланированных зависимостей, расползания конфигурации и изменений инфраструктуры, сделанных агентом без нужного контекста.</p><p>Аналогия с гонками здесь уместнее обычного: на малой скорости из заноса выходят легко, на большой одна ошибка стоит несопоставимо дороже. Отсюда идея <a href="https://devops.com/why-reliability-guardrails-are-needed-in-every-ai-coding-pipeline/">автоматических проверок надёжности</a> Колтона Эндруса: замкнутых циклов, которые безопасно создают настоящие условия отказа, проверяют устойчивость, предлагают исправление и перепроверяют его после внесения.</p><h3>Почему чтения кода недостаточно</h3><p>Ранняя идея инженерии хаоса состояла в том, что перебрать все сочетания отказов юнит-тестами и интеграционными тестами невозможно. Прогон реалистичного отказа против живого сервиса проверяет много вещей разом и потому эффективнее.</p><p>Для сгенерированного кода это верно вдвойне. Статический анализ доводит до определённой черты, но не отвечает на главный вопрос: что произойдёт, когда отвалится кеш или вырастет задержка у зависимости. Правильное поведение здесь описывается заранее (скажем, при недоступности кеша сервис обязан уйти в основную базу), и проверка сводится к тому, соблюдает ли кандидат уже существующую политику.</p><h3>Пять ресурсов, с которых начинают</h3><p>Львиная доля сбоев упирается в одни и те же пять вещей: процессор, память, диск, ввод-вывод и сеть. Программы работают на компьютерах, а у компьютеров набор ресурсов один и тот же. Отсюда и минимальный набор автоматических проверок:</p><ul><li>Избыточность по зонам и узлам: что происходит при отказе целой зоны, отдельного хоста или контейнера.</li><li>Масштабирование по процессору: корректно ли настроено расширение при всплеске и сворачивается ли оно обратно после.</li><li>Масштабирование по памяти: то же самое, включая корректную деградацию, если расшириться не удалось.</li><li>Отказ зависимости: как ведёт себя приложение, когда до внешнего или внутреннего сервиса не достучаться.</li><li>Задержка зависимости: сервис доступен, но отвечает медленно. Приложение деградирует аккуратно, уходит к запасному источнику или просто падает.</li></ul><p>Ответы на эти вопросы у зрелой команды обычно уже записаны в виде политик. Проверки не изобретают новых требований, они лишь удостоверяют, что очередной кандидат на выкатку этим требованиям соответствует.</p><h3>Результат проверки возвращается агенту</h3><p>Ставится это всё в конце конвейера сборки, прямо перед продвижением версии. Провалилась проверка — код помечается и уходит обратно вместе с описанием теста и предложенным исправлением.</p><p>Дальше начинается самое интересное. Данные о том, какая проверка провалилась, что исправили и прошла ли она после этого, возвращаются в агентский рабочий процесс как контекст. Проверять всё равно нужно каждый раз, но новый код начинает проходить проверки чаще, чем проваливать. Тот же журнал полезен и при разборе инцидентов: понятно, какие режимы отказа уже проверялись, с какими результатами и что было исправлено.</p><h2>Слой третий: границы информации</h2><p>Третий слой обсуждают реже всего, а он становится критичным ровно в тот момент, когда система обзаводится постоянной памятью о пользователе.</p><p>Речь о контекстной целостности — принципе из теории приватности Хелен Ниссенбаум: какие сведения уместно раскрывать при выполнении конкретной задачи. Домашний адрес нужен для оформления доставки и совершенно неуместен при составлении рабочего письма коллеге. Человек проводит эту границу не задумываясь, у модели с сохранённой памятью её приходится обеспечивать отдельно.</p><h3>Что показали измерения</h3><p>Шнайер <a href="https://www.schneier.com/blog/archives/2026/08/llms-and-contextual-integrity.html">разбирает две работы на эту тему</a>. Первая описывает бенчмарк CIMemories: синтетические профили пользователей больше чем со ста атрибутами каждый и набор задач, в которых один и тот же атрибут для одних задач необходим, а для других неуместен.</p><p>Результаты неутешительные. <a href="https://www.schneier.com/blog/archives/2026/08/llms-and-contextual-integrity.html">Передовые модели показывают до 69% нарушений</a> на уровне отдельных атрибутов, то есть раскрывают их там, где раскрывать не следовало. Снижение доли нарушений при этом обычно достаётся ценой полезности: модель становится осторожнее и хуже решает задачу.</p><p>Ещё важнее динамика. Нарушения накапливаются и по числу задач, и по числу прогонов: при росте использования с одной задачи до сорока доля нарушений у GPT-5 поднимается с 0,1 до 9,6%, а при пятикратном исполнении одного и того же промпта достигает 25,1%. Последняя цифра особенно неприятна: она означает нестабильное поведение, когда на идентичный запрос модель раскрывает разные атрибуты.</p><h3>Почему просьба быть осторожнее не помогает</h3><p>Естественная реакция инженера — добавить в системный промпт указание бережно относиться к персональным данным. Измерения показывают, что это не работает: модели переобобщают и начинают выдавать либо всё, либо ничего, вместо того чтобы принимать решение с учётом контекста.</p><p>Вывод авторов прямой: проблема не решается ни лучшим промптингом, ни увеличением масштаба модели, потому что требуется собственно рассуждение о контексте, в котором система сейчас работает.</p><h3>Что действительно даёт эффект</h3><p>Вторая работа как раз про это. Авторы сначала просят модель явно рассуждать о том, какие сведения уместны для текущей задачи, а затем закрепляют это обучением с подкреплением. На синтетическом наборе всего из 700 примеров с разнообразными контекстами и нормами раскрытия им удалось заметно сократить неуместное раскрытие, не потеряв в качестве решения задач, причём для моделей разных размеров и семейств.</p><p>Ценнее всего то, что улучшение переносится: обучение шло на синтетике, а результат подтвердился на устоявшемся бенчмарке с человеческой разметкой, оценивающем утечки в действиях и вызовах инструментов. Практический смысл для тех, кто строит систему поверх готовой модели, тот же, что и на двух предыдущих слоях: контроль границ реализуется в обвязке, а не выпрашивается у модели формулировками.</p><h2>Что забрать с собой</h2><p>Три слоя выглядят разнородно, но подчиняются одному правилу: свойство, которое вам нужно от системы, обеспечивается кодом вокруг модели, а не выбором модели. Маршрутизация даёт цену и приватность инфраструктуры: данные не покидают периметр. Автоматические проверки отказов дают устойчивость. Явный контроль границ информации даёт приватность самих данных пользователя, независимо от того, где физически стоит модель.</p><p>Проверить это на своём проекте несложно: посмотрите, сколько времени команда потратила за квартал на сравнение моделей и сколько на то, что находится между пользователем и моделью. Если первое число заметно больше, вы, скорее всего, оптимизируете не тот слой.</p><p>Материалы разбора: <a href="https://www.schneier.com/news/archives/2026/07/why-an-ai-harness-may-matter-more-than-the-latest-model.html">интервью Шнайера об обвязке и локальных моделях</a>, <a href="https://devops.com/why-reliability-guardrails-are-needed-in-every-ai-coding-pipeline/">проверки надёжности в конвейере с ИИ-кодом</a> и <a href="https://www.schneier.com/blog/archives/2026/08/llms-and-contextual-integrity.html">обзор двух работ о контекстной целостности</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>SKILL.state от Google и Пердью: агент тратит в 50 раз меньше токенов</title>
      <link>https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si</link>
      <comments>https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si</guid>
      <description><![CDATA[<p>Исследователи Google и Purdue University предложили SKILL.state: агент хранит вместо полной истории компактное JSON-состояние и патчит его на каждом шаге. На 200 шагах 122 тысячи токенов вместо 6,2 млн, точность 94% вместо 84%. Механизм, все цифры, ограничения и что можно повторить у себя.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/google-i-universitet-perdyu-pokazali-skill-state-v-skladskoj-si">SKILL.state от Google и Пердью: агент тратит в 50 раз меньше токенов</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 01 Sep 2026 17:28:20 GMT</pubDate>
      <content:encoded><![CDATA[<p>Исследователи из Google и Университета Пердью опубликовали на arXiv работу <a href="https://arxiv.org/abs/2608.26263">«SKILL.state: Scalable Long-Horizon Agent Skills»</a>, в которой предлагают ИИ-агенту не хранить в контексте всю историю действий. Вместо неё модель на каждом шаге получает три вещи: неизменную инструкцию навыка, компактное JSON-состояние задачи и последнее наблюдение, а отвечает патчем к состоянию и следующим действием. В складской симуляции на 200 шагов это дало в среднем по пяти запускам 122 тысячи токенов вместо 6,2 млн у агента с памятью-сводкой при росте точности с 84% до 94%. Первая версия статьи вышла 26 августа, вторая 28 августа; 1 сентября её пересказал vc.ru.</p><p>Если вы пишете агентов с накапливаемой историей (append-only), вы знаете эту боль: чем дольше сессия, тем дороже каждый следующий шаг, потому что модель заново перечитывает всё, что было. Работа интересна тем, что предлагает не сжимать историю, а заменить её структурой, и показывает на цифрах, что структура работает лучше сжатия. Оговорка сразу: результат сильно зависит от модели, а публичного кода авторы не выложили.</p><ul><li>Работа arXiv:2608.26263, авторы Санкет Бадхе, Приянка Тивари и Чонхён Чон из Google LLC и Purdue University; по пометке авторов на arXiv, принята на EMNLP.</li><li>На каждом шаге модель видит только инструкцию навыка, текущее JSON-состояние и последнее наблюдение; патч состояния проверяется, рассуждение после шага отбрасывается.</li><li>SkillExecBench (среда Warehouse Management) на Gemini 3 Flash: при 100 шагах 65 тысяч токенов против 1,25 млн у ReAct при точности 94% против 84%; при 200 шагах 122 тысячи против 6,2 млн у агента с памятью-сводкой.</li><li>При равном бюджете около 1 800 токенов на запрос простое усечение даёт точность 18%, LLMLingua 22%, SKILL.state 94%.</li><li>Ограничения: схема состояния задаётся заранее; задачи, где нужна сама история (аудит, объяснение действий), методу не подходят; один агент; публичного репозитория нет; на Gemma-4-31B и Qwen-3-8B при 100 шагах точность 42% и 34% против 94% у Gemini 3 Flash.</li></ul><h2>Как это устроено</h2><p>Обычный агент в стиле ReAct на шаге t получает весь журнал: системный промпт, все прошлые команды, все ответы инструментов и свои рассуждения. Суммарно за T шагов это O(T²) токенов, потому что каждый шаг перечитывает всё предыдущее. В SKILL.state промпт шага состоит из трёх частей, размер которых не зависит от номера шага при условии, что состояние и наблюдение ограничены по объёму: спецификация навыка P (что агент умеет и как должен себя вести), структурированное состояние Σ (JSON с тем, что важно для задачи) и последнее наблюдение O. Модель отвечает рассуждением, JSON-патчем к состоянию и действием; патч детерминированно проверяется и применяется, рассуждение после этого выбрасывается, действие исполняется.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-09-02/c899b3a4-bb38-4eac-ace6-16f77efea3b5.webp" alt="Схема одного шага агента SKILL.state" /><figcaption>Один шаг агента в SKILL.state: модель получает инструкцию, состояние и наблюдение, отдаёт патч состояния и действие, рассуждение отбрасывается. Схема: Tproger по статье arXiv:2608.26263.</figcaption></figure><p>Патч применяется операцией слияния: новые ключи добавляются, существующие перезаписываются, значение null удаляет ключ. Итог — суммарные затраты O(T) по токенам, а размер одного промпта почти не растёт: в экспериментах он держался в диапазоне 1 736–1 905 токенов при горизонте от 10 до 200 шагов. Схема состояния пишется один раз на домен, а не на задачу: для всех 100 задач InterCode CTF агент использовал одну статическую схему из пяти полей — найденные флаги, проверенные гипотезы, активные файлы, рабочий каталог, сводка команд.</p><h2>Цифры из работы</h2><p>Основной эксперимент — SkillExecBench, среда Warehouse Management: симуляция склада с длинными последовательностями действий, модель Gemini 3 Flash, пять случайных сидов, температура 0. При 100 шагах ReAct потратил 1 245 413 токенов при точности 0,84, baseline с явным состоянием в стиле LangGraph — 1 062 387 токенов и 0,91, SKILL.state — 65 408 токенов и 0,94. При 200 шагах агент с памятью-сводкой (Memory) ушёл в 6 175 509 токенов при точности 0,84, а SKILL.state уложился в 122 384 токена при 0,94. Именно отсюда «в 50 раз меньше» из пересказов; в дополнительном эксперименте с репозиторием кода при 100 шагах разница составила 1 848 500 против 90 200 токенов.</p><p>Авторы связывают выигрыш в точности не с сокращением текста, а с тем, что состояние сохраняет отношения между сущностями: какой шаг уже выполнен, что заблокировано, что кому должно. Два результата важнее заголовочных. Первый: при одинаковом бюджете около 1 800 токенов на промпт обычное усечение истории дало точность 0,18, сжатие через LLMLingua — 0,22, а SKILL.state — 0,94. Второй: в тесте с 50 нерелевантными событиями на каждом шаге обычный агент упал до 0,53, а SKILL.state удержал 0,98, потому что шум не попадает в состояние. После внешнего изменения среды и корректирующего уведомления SKILL.state в трёх сценариях из четырёх восстанавливался за 0 шагов, остальные подходы ошибались ещё 5–8 шагов; со сценарием отменённого заказа не справился никто.</p><p>На более привычных бенчмарках выигрыш скромнее, но есть: на InterCode CTF 54,2% Pass@1 против 43,2% у ReAct и 41,8% у Stateful, на τ-Bench Retail 58,3% и Airline 32,4% при снижении токенов до 3,47 млн и 2,88 млн соответственно.</p><h2>Чего в работе нет</h2><p>Ограничения авторы называют сами. Метод предполагает, что всё важное для будущих шагов можно вовремя записать в заранее заданную схему состояния: если структура задачи заранее неизвестна, если значимость детали осознаётся позже или если нужна сама история (аудит, происхождение данных, объяснение действий), подход не подходит. Второе: реализация рассчитана на одного агента, для нескольких понадобится разрешение конфликтов при одновременных записях в состояние. Третье: архитектура не привязана к Gemini, но качество сильно зависит от модели: при 100 шагах Gemini 3 Flash дала точность 0,94, Gemma-4-31B-it — 0,42, Qwen-3-8B-it — 0,34. Четвёртое, практическое: публичного репозитория авторы на 1 сентября не указали, хотя в статье есть промпты, псевдокод рантайма и детали реализации.</p><h2>Что можно повторить у себя</h2><ul><li>Опишите для своей задачи схему состояния из полей, которые реально нужны для следующего шага, и просите модель возвращать патч к ней, а не свободный текст; авторам для CTF хватило пяти полей.</li><li>Отбрасывайте рассуждения модели после шага: они нужны для выбора действия, а не для памяти.</li><li>Передавайте только последнее наблюдение целиком; всё, что из прошлых наблюдений имеет значение, должно уже лежать в состоянии.</li><li>Заведите тест на шум: подмешивайте в наблюдения нерелевантные события и смотрите, не тащит ли агент их в состояние.</li><li>Не ждите тех же 50 раз: в статье это симуляция склада на Gemini 3 Flash; на τ-Bench экономия заметно меньше, и ваш случай ближе ко второму.</li></ul><h2>Контекст</h2><p>Идея держать в контексте состояние вместо истории не нова: похожие приёмы есть в LangGraph и в собственных рантаймах команд, которые строят агентов в проде. Вклад работы в том, что она измеряет разницу на длинных горизонтах и сравнивает с методами сжатия при равном бюджете. Обсуждения на Hacker News и в блоге Google Research на момент публикации нет; следующий сигнал — появится ли код или воспроизведение на других моделях.</p><p>Источники: <a href="https://arxiv.org/abs/2608.26263">SKILL.state: Scalable Long-Horizon Agent Skills (arXiv:2608.26263)</a>, <a href="https://vc.ru/ai/3115725-google-i-universitet-perdyu-ii-agenta-s-ekonomiej-tokenov">Пересказ vc.ru</a></p><p>Изображение на обложке: Google</p>]]></content:encoded>
    </item>
    <item>
      <title>F.A.Q. о профессии «Архитектор решений»</title>
      <link>https://tproger.ru/articles/f-a-q-o-professii-arhitektor-rewenij</link>
      <comments>https://tproger.ru/articles/f-a-q-o-professii-arhitektor-rewenij?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/f-a-q-o-professii-arhitektor-rewenij</guid>
      <description><![CDATA[<p>Кто такой архитектор решений, чем занимается, как попасть в профессию и пройти собеседование. Опыт из Альфа-Банка.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/f-a-q-o-professii-arhitektor-rewenij">F.A.Q. о профессии «Архитектор решений»</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Карьера]]></category>
      <category><![CDATA[Собеседование]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 27 Aug 2026 12:19:39 GMT</pubDate>
      <content:encoded><![CDATA[<p>Всем привет, я Михаил Салахов, руководитель направления архитектуры решений. В сфере информационных технологий работаю больше 20 лет, и последние 5 занимаюсь архитектурой в Альфа-Банке. За свою карьеру успел поучаствовать в огромном количестве проектов в различных ролях от инженера программиста до руководителя технического подразделения.</p><p>Пройдёмся по дорожной карте:</p><ul><li>Архитектор решений — кто это?</li></ul><ul><li>Чем занимается архитектор решений и какие границы ответственности?</li></ul><ul><li>Откуда приходят архитекторы решений?</li></ul><ul><li>Откуда брать знания?</li></ul><ul><li>Что ждет на собеседовании?</li></ul><ul><li>Как пережить онбординг?</li></ul><ul><li>Как проходит первый год архитектора решений?</li></ul><ul><li>Какой рабочий день архитектора решений?</li></ul><ul><li>Какие перспективы у архитектора решений?</li></ul><h2>Архитектор решений — кто это?</h2><p><b>Архитектор решений — это IT-специалист, отвечающий за проектирование комплексных технологических решений, удовлетворяющих потребности компании или клиента. Он находит компромисс между долгосрочными и стратегическими инициативами развития ИТ ландшафта и текущими задачами которые стоят у бизнеса.</b></p><p>Ключевая задача архитектора решений — предложить решение бизнес идеи с учетом будущих изменений ИТ ландшафта и разработать проектное решение (документ), из которого будет понятно какие системы нужно доработать и как. При проектировании особенно важно иметь практический опыт, потому что ошибки, которые допускаются на ранней стадии проекта, очень тяжело исправлять на стадии реализации.</p><p>Все архитекторы относятся к подразделению информационных технологий, но видят ИТ ландшафт предприятия по-разному.</p><ul><li>Корпоративный архитектор видит ИТ ландшафт всего предприятия, понимает направления долгосрочного развития и вносит изменения, влияющие на инфраструктуру всего ландшафта в долгосрочной перспективе (3-5 лет).</li></ul><ul><li>Архитектор решений видит ИТ ландшафт всего предприятия и проектирует решения в рамках задач бизнес-направления, за которым он закреплен, строго в соответствии с долгосрочной стратегией развития ИТ ландшафта в целом. Понимает цели и задачи бизнеса и предлагает решения, в соответствии с которым производятся изменения ИТ ландшафта в ближайшей перспективе (около года).</li></ul><ul><li>Системный архитектор занимается развитием одной или нескольких систем: отвечает за правильную, масштабируемую архитектуру на более детальном уровне. Архитектор системы ближе к технологиям и ПО: применяемым базам данных, использованию правильных брокеров очередей, правильному проектированию api доступа и другим техническим деталям систем.</li></ul><ul><li>Технический архитектор — отвечает за оборудование, сервера, техническое обеспечение всего, что необходимо для функционирования ИТ ландшафта.</li></ul><p>Каждый архитектор на своем уровне контролирует правильное развитие ИТ ландшафта той области, которая находится в его зоне ответственности.</p><h2>Чем занимается архитектор решений и каковы границы его ответственности?</h2><p>В поле зрения архитектора решений находится весь ИТ ландшафт банка и понимание о функциональных границах всех систем.</p><p><b>Первая задача архитектора решений — выявить все функции, которые необходимо проанализировать при реализации бизнес задачи.</b></p><p>При поступлении запроса на разработку архитектуры архитектор решений анализирует поступившие требования и производит их декомпозицию, выявляет функции, которые необходимо реализовать. Что будет, если архитектор ошибётся и не учтёт доработки в одной или нескольких системах? Их не учтут в бюджете проекта, не проведут изменения этих процессов, что, скорее всего, приведёт к сдвигу сроков, увеличению бюджета, а это всегда воспринимается бизнесом не очень хорошо.</p><p><b>Вторая основная задача архитектора — разложить все требуемые функции по правильным системам.</b></p><p>После анализа бизнес-требований и выявления функций архитектор решений распределяет их по системам. Если для выполнения тех или иных функций требуется разработка новой системы или модуля, архитектор решений прорабатывает этот вопрос с корпоративным архитектором и согласовывает появление модуля или системы в ИТ ландшафте предприятия.</p><p><b>Третья основная задача архитектора решений спроектировать необходимые интеграции между системами и модулями, или взаимодействие с внешними системами.</b></p><p>Когда все функции разложены по нужным системам, архитектор прорабатывает интеграции между системами и модулями для того, чтобы был правильный информационный обмен данными, событиями, уведомлениями и другими бизнес сущностями.</p><p>На всех этапах проектирования архитектор решений учитывает требования большого количества стейкхолдеров, включая кибербезопасность, корпоративную архитектуру, ответственных за развитие систем. Архитектор решений прорабатывает требования по обеспечению нагрузки, уровня критичности систем и процессов, возможности быстрого масштабирования решения по будущим запросами бизнеса.</p><p>Результатом работы архитектора решений является документ - проект решения, или как мы его называем <b>архитектурный вижен.</b> Что в него входит?</p><p><b>№1. Диаграмма, на которой изображаются:</b></p><ul><li>состав систем, которые задействованы в решении бизнес-задачи;</li><li>бизнес-данные, которые передаются между системами;</li><li>интеграционные потоки между системами;</li><li>протоколы и способы интеграций.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-08-27/8bba1ac5-79ff-4c8e-a9f7-0de05608ceb1.webp" alt="" /></figure><p><b>№2. Диаграмма процессов и функций, которые задействованы в решении:</b></p><ul><li>изменения бизнес-процессов;</li><li>изменения функций систем;</li><li>роли, задействованные в бизнес-процессах.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-08-27/83081254-7679-47cc-ab49-6f8af39cf777.webp" alt="" /></figure><p>Архитектурный вижен — верхнеуровневый взгляд на то, как изменится ИТ ландшафт предприятия при реализации бизнес-задачи и что нужно сделать, чтобы эту задачу реализовать. Это не проект по факту реализации, это концепт, который уточняется на более позднем этапе разработки. В TOGAF это фаза preliminary.</p><h2>Откуда приходят архитекторы решений?</h2><p>Попасть в архитекторы можно из различных отраслей и должностей.</p><p>К нам приходят руководители структурных подразделений различных компаний, руководители разработки, руководители продуктовых команд, аналитики из государственных компаний, из компаний интеграторов, и компаний разработчиков ПО.</p><p>Главное — сочетать навыки работы с людьми, технических знания, опыт работы в проектах. Навыки работы с людьми нужны для работы в команде — архитектор очень много договаривается и ищет компромиссы, иногда даже модерирует команды и отвечает на вопросы, когда команда приступает к реализации. Технические знания нужны для содержательного общения на разных уровнях. Архитектор решений ведет переговоры с разработчиками, лидерами команд, аналитиками, ИТ лидерами, с техническими директорами различных направлений, с руководителями бизнес направлений, корпоративными архитекторами, системными архитекторами.</p><h2>Где брать знания?</h2><p>Архитектор решений прежде всего практик. Особенно ценны практические знания и опыт в следующих областях:</p><ul><li>реализации крупных высоконагруженных проектов в банковской сфере;</li><li>практический опыт применения различного программного обеспечения и обоснование выбора технологических платформ;</li><li>опыт работы в интеграционных проектах.</li></ul><p>Почему важен опыт работы с банками? В финтехе достаточно уникальное сочетания жестких требований к безопасности, выполнения требований законодательства и различных предписаний ЦБ, надежности и доступности инфраструктуры, гибкости и скорости к внедрению нового функционала. Очень важно понимать на практике баланс между сроками реализации проекта и целями, которые достигает долгосрочная целевая архитектура в рамках предприятия, и необходимостью масштабирования решения на другие похожие задачи.</p><p>Очень пригодятся знания технологических платформ, например:</p><ul><li>брокеры очередей, когда они применяются, какие их отличия и варианты использования;</li></ul><ul><li>базы данных, их отличия и варианты использования;</li></ul><ul><li>средства и способы защиты каналов связи;</li></ul><ul><li>программные продукты исполнения бизнес-процессов, их отличия и варианты использования.</li></ul><h2>Чего ждут на собеседовании?</h2><p>Первый этап при любом трудоустройстве – техническое интервью. Чтобы попасть на техническое интервью резюме кандидата должно заинтересовать. Что привлекает внимание в резюме:</p><ul><li>Опыт работы в банковской сфере. Это не обязательный, но крайне желательный, опыт, который необходимо отразить в резюме, если он есть. При этом не обязательно работать в банковской сфере, подойдет и опыт работы с банками (например, совместные проекты или работа на проекте). Если такой опыт есть, его обязательно надо указать в резюме.</li></ul><ul><li>Образование. Базовое образование архитектора решений – техническое. Приветствуется опыт работы с ПО в роли разработчика, специалиста по сопровождению или любая другая техническая роль, которая имеет возможность на практике освоить работу различных приложений.</li></ul><ul><li>Опыт работы на руководящих позициях. Его не должно быть ни много ни мало. Управленцу, не имеющему практического опыта и навыков работы с программным обеспечением, будет сложно договариваться, потому что иногда нужно понимать проблемы команд и систем и предлагать решения исходя из собственного опыта.</li></ul><p>Архитектор решений работает на начальном этапе проекта. На этом этапе очень важно понимать проектную деятельность, понимать бюджет проекта, особенности взаимодействия с вендорами или командами, которые работают в разных организационных моделях. Архитектурный вижен содержит указания на множество систем, а за системами закреплены команды развития, которые могут работать по-разному. Одним командам нужны спецификации и подробные технические задания, другим — нужно всё зафиксировать в запросе и не менять, потому что они передают работу вендору, третьи могут позволить себе гибкость и менять требования каждые 2 недели. Все команды, задействованные в проекте, контактируют с архитектором решений и архитектурный вижен — это документ, который всех объединяет.</p><p>Техническое интервью — это не только проверка ваших навыков, но и возможность получить ценный опыт, узнать что-то новое. На собеседовании кандидату задают вопросы по стандартам, очередям, оркестраторам, базам данных, паттернам, вопросам информационной безопасности, языкам программирования. У нас много практических кейсов, которые мы предлагаем решить на собеседовании, в процессе решения проверяем, как архитектор может быстро и правильно формировать «архитектуру на салфетке». Всё как в реальной жизни.</p><h3>Советы по прохождению технического интервью:</h3><ul><li>Подключайтесь с видео. Это обязательный элемент в каждом собеседовании. Без видео скорее всего собеседование придется перенести.</li><li>Не бойтесь рассуждать и отстаивать свое решение. Многие вопросы не имеют правильного ответа, иногда необходимо предложить несколько вариантов решения и сравнить. В конце концов вы должны уметь брать ответственность за свои решения и уметь не дать завести себя в тупик во время защиты архитектуры. При решение практических задач так же проявляются такие важные навыки как умение вести переговоры, умение чувствовать, что вы где-то можете быть не правы, умение адаптироваться под меняющиеся, иногда противоречащие требования к задаче.</li><li>Вас могут попросить нарисовать и расшарить экран, поэтому рекомендуем подключаться к интервью с ноутбука или стационарного компьютера, и закрыть все лишние программы.</li></ul><p>Если вам дали практический кейс, подумайте, какой информации не хватает для принятия решения и почему. Архитектор — специалист высокой квалификации. Он должен уметь выяснять все, что необходимо для решения поставленной задачи.</p><h3>Что категорически нельзя делать на собеседовании:</h3><ul><li>Подсматривать или «незаметно» искать ответы на вопросы во время собеседования. Это всегда заметно и скорее всего больше навредит, чем поможет. Все вопросы на опыт, а не на теорию. Всегда можно честно признаться, что вы чего-то не знаете, и пусть работодатель принимает решение насколько отсутствие этих навыков критично. Честность иногда полезнее, чем ответ на вопрос.</li><li>Использовать ИИ или другие программы, которые помогут вам с ответами на вопросы. Искусственный интеллект может помочь с теорией, но, если вы справляетесь с теорией, вы также должны хорошо решать и практические примеры или кейсы, в который надо применять опыт. Если одно с другим не стыкуется, значит работать вы все равно не сможете на этой позиции. Архитектор решений прежде всего практик.</li></ul><h2>Как пережить онбординг?</h2><p>Онбординг — начальный этап, во время которого архитектор знакомится с ИТ ландшафтом предприятия и решает первую задачу.</p><p>В Альфа-Банке со всеми будущими сотрудниками связывается наставник, помогает собрать необходимые документы и не потеряться в первый день работы. Архитектор, который выходит на работу, знает к кому можно обратиться по любым вопросам.</p><p>В первый день работы каждому архитектору передают план онбординга. В плане есть все необходимое для начала работы, к примеру, ссылки на материалы на портале архитектуры. Но также в план входят обязательные блоки:</p><ul><li>Изучение процессов. Огромная библиотека процессов и информация о том, как надо работать. Знание процессов очень помогает в работе, так как всегда понимаешь, как надо поступить в той или иной ситуации.</li><li>Блок знакомства. Архитектор знакомится с ИТ лидером, с корпоративным архитектором и руководителями проекта. С бизнес-подразделением архитектор знакомится в процессе работы.</li></ul><p>На период онбординга архитектору сразу выдается реальная задача – разработать архитектуру проекта, который впоследствии будет реализовываться. Это может быть новая архитектура проекта или внесение изменений в уже ранее согласованный документ. В любом случае архитектор проходит все фазы работы от проработки требований до согласования решения.</p><p>Архитектор решений — специалист достаточно самостоятельный. Он должен уметь сам находить необходимую информацию в многочисленных источниках, сам контактировать с командами развития, и самостоятельно заниматься поиском нужной информации.</p><p>Особенно следует подчеркнуть, что ИТ ландшафт предприятия непрерывно развивается с момента образования предприятия. Архитектор решений не проектирует решение изолированно от других систем. Даже если архитектор решений создает новую систему, она не функционирует изолированно. Поэтому архитектор решений постоянно занимается анализом существующих систем и процессов, даже если они очень давно были разработаны.</p><h3>После завершения онбординга архитектора решений ждет проверка знаний.</h3><p>Что входит в проверку:</p><ol><li>Технические вопросы. Для архитектора важно поддерживать знания об актуальном стеке технологий, применяемых в проектах, актуальные знания о способах интеграций, протоколах передачи данных, а также актуальные знания о программном обеспечении, которое есть на рынке, и вариантах его применения.</li><li>Знание процессов. Без этих знаний архитектор не сможет работать. Процессы надо знать во всех нюансах и вариантах их применения, чтобы в любой ситуации архитектор мог принимать правильное решение.</li><li>Знания паттернов.</li><li>Знания технологического ландшафта.</li></ol><h2>Как проходит первый год архитектора решений?</h2><p>Онбординг считается завершенным по истечении 3 х месяцев. К этому времени можно не только разработать и согласовать архитектуру для реального проекта, но и поучаствовать в ранних фазах реализации проекта. К завершению онбординга архитектор работает над несколькими задачами одновременно и у него формируется достаточно внушительный бэклог задач.</p><p>Задачи в беклоге могут быть разные по степени проработки — главная задача архитектора следовать процессам и вместе с ИТ лидером работать с приоритетами.</p><p>Уметь работать по процессам в данной ситуации — один из основных навыков который приобретается на ранней стадии. Что важно помнить:</p><ul><li>Всегда нужно фиксировать вопросы и ответы, которые появляются к требованиям. Некоторые ответы могут сильно изменить архитектуру.</li></ul><ul><li>Переключаться между задачами разных приоритетов — это то, что придется делать постоянно.</li></ul><ul><li>Все задачи мы ведем так, чтобы любой мог открыть задачу и посмотреть ее историю.</li></ul><ul><li>Если задача отложена или отменена по инициативе бизнеса мы фиксируем причину, что бы заказчик не забыл о своем решении, когда вернется к задаче. Карточка задачи в реальной работе превращается в протокол, который всегда можно проанализировать и предъявить в случае необходимости</li></ul><ul><li>Архитектуру надо прорабатывать так, чтобы всегда можно было открыть документ и понять какие изменения нужно сделать.</li></ul><p>Основная задача бизнеса — сделать как можно лучше для клиента. Если в процессе работы над задачей бизнес поймет что фича должна выглядеть совершенно по-другому бизнес без колебаний полностью переделают требования даже если задача выполнена и готова к установке в промышленную среду.</p><p><b>Полезный навык, который помогает в работе архитектора — умение управлять ожиданиями.</b> Если внесение незначительного изменения в требования приводит к существенным изменениям в проект, нужно сразу же достаточно четко донести этот факт всем заинтересованным стейкхолдерам с объяснениями причин. Формировать неоправданные ожидания у стейкхолдеров это практически преступление для архитектора решений.</p><p>В процессе работы архитектор узнает все больше об ИТ ландшафте предприятия и достаточно быстро приходит понимание, что он очень сложный и постоянно меняющийся. Причем изменения происходят по всему ландшафту практически одновременно. Есть проекты с сильным влиянием на ИТ ландшафт, а есть проекты со слабым влиянием. Когда архитектор проектирует решение, он должен быть в курсе не только изменений в рамках своего непосредственного участка, но и всех соседних. Это актуально особенно сейчас, когда очень много проектов связанных с импортозамещением.</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-08-27/8de12663-3e7c-4d1c-b5bb-f1356e862fac.webp" alt="" /></figure><h2>Каков рабочий день архитектора решений?</h2><p>Если в начале онбординга рабочий день архитектора выглядит вот так:</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-08-27/8a705fcd-d587-4d8c-8cc1-a27940251de3.webp" alt="" /></figure><p>То стандартный рабочий день архитектора решений выглядит примерно так:</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-08-27/7e0e053e-1e78-4929-bfc6-02fb35119089.webp" alt="" /></figure><h3>Почему так происходит?</h3><p>Архитектора решений активно привлекают на встречи для решения как задач бизнеса так и задач развития архитектурных практик. Везде командная работа. Задачи бизнеса приходят постоянно, архитектор берет задачу из бэклога и анализирует требования. Если требования не полные мы фиксируем вопросы и берем следующую задачу. После уточнения требований бизнес уже не ждет и приходится уже участвовать в 2 х, 3х, 4х   проектах одновременно. После того как архитектура готова архитектор консультирует команды на этапе архитектурного надзора, когда проект начинает реализовываться.</p><p>Руководитель направления контролирует загруженность архитектора. Раз в квартал архитектор общается один на один с руководителем, и можно открыто высказать, что мешает работе, какая нагрузка, что можно улучшить и взять задачу по улучшению процесса или инструмента. Руководитель направления все фиксирует и старается помогать улучшить ситуацию по мере возможности.</p><h2>Каковы перспективы архитектора решений?</h2><p>Рано или поздно архитектор задается вопросом о том, куда можно вырасти. Стоит сразу отметить, что рост архитектора не такой динамичный и бурный как у разработчика, или аналитика в силу того, что изначально архитектор человек уже с большим опытом. Чем дольше архитектор работает в Альфа-Банке, тем лучше он знает ландшафт предприятия, тем выше его ценность.</p><p><b>Первый путь роста</b> — поучаствовать в ещё большем количестве направлений, освоить ещё больше доменов и попробовать себя в новых проектах. Как правило некоторые проекты позволяют по-новому взглянуть на архитектурный ландшафт. Тут самое интересное это новые системы, новые процессы и новые ведения переговоров.</p><p><b>Второй путь роста</b> — попробовать себя в одном или нескольких направлениях развития архитектурных практик: развить методологию или поучаствовать в разработке нового инструмента. По согласованию с руководителем и при наличии возможности можно взяться за лидирование практик. Это позволит развить навыки проектного управления и ведения проекта.</p><p><b>Третье направление</b> — выбрать путь в сторону руководства людьми и стать функциональным руководителем. Но надо понимать, что руководитель помимо своей основной деятельности архитектора ещё имеет дополнительные функциональные обязанности, связанные с управлением людьми, бюджетами и другими административными задачами.</p><p><b>Четвертое направление</b> — стать корпоративным архитектором. Деятельность корпоративного архитектора совершенно иная в силу повышенного масштаба и уровня ответственности за решения.</p><p>Инструменты и процессы постоянно совершенствуются и меняются, вносят в нашу жизнь много яркого и позитивного. IT ландшафт тоже меняется, растёт, развивается вместе с бизнесом. Структура бизнеса живет невероятной жизнью, которая неразрывно связана с жизнью архитектора решений. <b>Высокие скорости принятия решений умноженные на масштаб предприятия делают жизнь архитектора решений в Альфа-Банке действительно невероятной.</b></p>]]></content:encoded>
    </item>
    <item>
      <title>7 стратегий модернизации легаси: что рефакторить, переносить, заменять или переписывать</title>
      <link>https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za</link>
      <comments>https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za</guid>
      <description><![CDATA[<p>7 стратегий модернизации легаси: когда оставить как есть, а когда рефакторить, переносить или переписывать. Практический гайд.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za">7 стратегий модернизации легаси: что рефакторить, переносить, заменять или переписывать</a>»</p>]]></description>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 25 Aug 2026 08:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Переписать проект с нуля — первое, что придет вам в голову, но по факту это месяцы работы без новых функций и без гарантии, что новая версия окажется лучше старой. Часто лучше обновить стек, разобрать один проблемный модуль или постепенно вынести часть логики в отдельный сервис.</p><h2>Сначала понять, нужно ли трогать систему</h2><p>Если приложение выдерживает нагрузку, получает исправления безопасности и не мешает выпускать новые функции, спешить с модернизацией незачем. Работает и работает.</p><p>Проблемы начинаются, когда разработчик меняет один модуль и роняет соседние, релизы начинают выкатываться реже, а поддержка системы забирает больше времени. Или большой монолит может долго запускаться, плохо масштабироваться и больно реагировать на сбой внутри одного модуля. Причин бывает сколько угодно, но стратегию подбирают под каждую отдельно. Где-то хватит новой версии фреймворка, где-то придётся отделять авторизацию, где-то — выносить отчёты в свой сервис.</p><p>Поэтому начинают с проблемы:</p><ul><li>старый стек больше не получает обновления;</li><li>новые функции делаются слишком долго;</li><li>систему трудно тестировать или масштабировать;</li><li>сбой одного модуля затрагивает всё приложение;</li><li>на поддержку уходит всё больше людей и рабочих часов.</li></ul><p>Всё это можно измерить, проверить и объяснить бизнесу. Дальше — варианты.</p><h2>1. Оставить систему как есть</h2><p>Иногда самое разумное решение состоит в том, чтобы ничего не менять.</p><p>Допустим, приложение решает одну понятную задачу, нагрузка на него почти не растёт, а новые функции добавляют пару раз в год. Переписывать такую систему только из-за возраста кода смысла мало: команда потратит квартал, а бизнес получит примерно ничего.</p><p>Следить за ней всё равно придётся. Обновления безопасности нужно ставить, резервные копии проверять, а документацию по запуску, интеграциям и восстановлению держать в актуальном состоянии.</p><p>Заранее стоит договориться, какое событие станет сигналом к началу модернизации. Например:</p><ul><li>платформа или библиотека перестала получать исправления безопасности;</li><li>поддержка стала занимать больше времени, чем раньше;</li><li>система перестала выдерживать нагрузку;</li><li>нужную бизнесу функцию нельзя добавить без большой переделки;</li><li>в команде не осталось людей, которые понимают код.</li></ul><p>Пока ничего из этого не случилось, систему оставляют в текущем виде.</p><h2>2. Вывести ненужную часть из эксплуатации</h2><p>Иногда часть легаси-системы уже никому не нужна: старый модуль, забытый API или интеграция с сервисом, который давно закрылся. Такой компонент проще отключить, чем поддерживать его дальше и тем более куда-то переносить.</p><p>Сначала придётся разобраться с зависимостями, потому что команда может просто не знать, что модуль дёргают соседние сервисы или другой отдел. Перед отключением стоит:</p><ul><li>проверить логи и запросы к компоненту;</li><li>найти связанные сервисы и задания;</li><li>узнать, какие команды пользуются его данными;</li><li>подготовить способ быстро вернуть его обратно.</li></ul><p>После проверки компонент выключают на тестовый период, и если за это время ошибок не появилось, код и инфраструктуру удаляют. Данные, которые ещё могут понадобиться, перед этим складывают в архив.</p><h2>3. Обновить стек без смены архитектуры</h2><p>Система остаётся монолитом: команда не делит её на микросервисы и не трогает основные связи между модулями. Меняется техническая начинка, то есть язык, фреймворк, библиотеки, база данных или среда выполнения.</p><p>Например, приложение переводят на поддерживаемую версию .NET, но оно по-прежнему остаётся одним приложением. Такой вариант подходит, когда архитектура со своими задачами пока справляется, а болит именно устаревшая технология.</p><p>Просто поменять номер версии в конфиге обычно не получается, потому что старые библиотеки отказываются работать с новым фреймворком, а часть API меняется или исчезает вовсе. Поэтому сначала собирают список зависимостей и смотрят, что из этого вообще можно обновить.</p><p>Дальше компоненты обновляют по очереди, прогоняя тесты после каждого шага. Отдельно проверяют интеграции, работу с базой данных и поведение под нагрузкой. Новую версию сначала разворачивают в тестовой среде, а для выката в продакшен готовят план отката.</p><p>Проблемы самого монолита при этом никуда не денутся: если модули сильно связаны между собой или приложение плохо масштабируется, одним обновлением стека дело не обойдётся.</p><h2>4. Перенести систему на другую платформу</h2><p>Здесь код приложения почти не меняют, меняется место, где оно работает. Например, систему можно перенести с физических серверов на виртуальные машины или в облако. Другой вариант — запустить приложение в контейнерах. Архитектура при этом остаётся прежней: монолит не превращается в набор микросервисов.</p><p>Перед переносом нужно проверить требования к процессору, памяти, дискам и сети. Ещё нужно учесть внешние сервисы, настройки безопасности и доступ к базе данных. Затем систему разворачивают на новой платформе и проверяют под рабочей нагрузкой.</p><p>Такой перенос сам по себе не ускоряет приложение. Если проблема находится в коде или базе данных, смена инфраструктуры её не решит. Зато команда может отказаться от устаревших серверов и упростить дальнейшую поддержку системы.</p><p>Старую среду лучше отключать только после проверки новой. Если при запуске возникнут ошибки, трафик можно будет вернуть обратно.</p><h2>5. Заменить готовым продуктом</h2><p>Некоторые внутренние системы дешевле заменить готовым решением. Типичная ситуация: компания годами поддерживает собственный сервис, хотя продукт с теми же функциями давно можно купить или взять по подписке.</p><p>Начинают со сравнения расходов. С одной стороны, зарплаты разработчиков, серверы, исправление ошибок и обновления своей системы. С другой, лицензия, внедрение, перенос данных и доработки готового продукта.</p><p>Дальше сверяют функции, потому что готовое решение может не поддерживать часть внутренних процессов или нужные интеграции. Команда составляет список обязательных возможностей и проверяет их до перехода, пока договор ещё не подписан.</p><p>Отдельно разбираются с данными: что переносить, в каком формате и как проверить результат. Старую систему держат включённой, пока пользователи не начнут работать в новой, а данные и интеграции не пройдут проверку.</p><h2>6. Рефакторить по частям</h2><p>При рефакторинге поведение системы снаружи сохраняется, а внутри код меняется: команда упрощает структуру, разделяет связанные компоненты, убирает дублирование и обновляет отдельные зависимости.</p><p>Переписывать для этого весь проект не нужно. Сначала выбирают модуль, который часто меняется, собирает много ошибок или мешает добавлять новые функции. Затем фиксируют его текущее поведение тестами и только после этого берутся за код.</p><p>Работу делят на небольшие этапы, и после каждого запускают тесты, чтобы убедиться, что система ведёт себя как раньше. Если что-то всё-таки сломалось, маленькое изменение откатить куда проще, чем трёхнедельный коммит.</p><p>Чтобы выбрать модули, нужно понимать их зависимости, частоту изменений и роль в бизнес-процессах. Когда внутри компании таких специалистов нет, зовут внешнюю команду.<a href="https://centicore.ru/services/legacy-modernization/"> Centicore</a> занимается модернизацией легаси-кода, баз данных и инфраструктуры, в услугу входят рефакторинг кодовой базы и обновление технологического стека.</p><h2>7. Переписать систему постепенно</h2><p>Систему можно заменять по одному модулю за раз, оставляя остальное работать как прежде. Подход называется Strangler Fig, по имени фикуса-душителя, который оплетает дерево и со временем занимает его место.</p><p>Новые функции сразу пишут на новом стеке, какое-то время старая и новая части работают параллельно, поэтому запросы приходится направлять в нужную версию системы. Новый сервис при этом может ходить в прежнюю базу данных, и если такая схема начинает мешать работе или масштабированию, данные позже переезжают в отдельную БД.</p><p>Когда функция переехала, старый модуль отключают. Монолит понемногу уменьшается, а новая система забирает его задачи.</p><h2>Как выбрать стратегию</h2><p>Для всей системы один подход выбирать необязательно. Стабильный модуль можно не трогать, старую интеграцию удалить, а авторизацию постепенно вынести из монолита.</p><p>Сначала каждую часть системы проверяют по нескольким критериям:</p><ul><li>как часто в ней меняют код;</li><li>сколько времени уходит на поддержку;</li><li>от каких сервисов и баз данных она зависит;</li><li>есть ли автоматические тесты;</li><li>сколько времени она может не работать;</li><li>хватает ли у команды людей и знаний для изменений.</li></ul><p>Рефакторинг подойдёт коду, который трудно менять, хотя заменять его целиком нет смысла. Когда готовый продукт закрывает те же задачи, считают стоимость перехода. А если архитектура уже мешает выпускать функции и масштабировать систему, отдельные модули переписывают постепенно.</p><h2>Что подготовить до начала работ</h2><p>Работу делят на этапы, и каждый должен давать результат, который можно проверить и выпустить отдельно. Задачу длиной в несколько месяцев тяжело оценивать, тестировать и откатывать.</p><p>До начала изменений нужно:</p><ul><li>составить список модулей, сервисов, баз данных и интеграций;</li><li>найти команды, которые пользуются системой;</li><li>проверить автоматические и ручные тесты;</li><li>измерить текущую нагрузку и время ответа;</li><li>определить порядок выпуска изменений;</li><li>подготовить откат к рабочей версии.</li></ul><p>Тестировать отдельные модули недостаточно. После изменений проверяют интеграции, работу с базой данных, нагрузку и основные пользовательские сценарии.</p><h2>Итого</h2><p>Легаси необязательно переписывать целиком. Сначала стоит понять, какая именно часть системы мешает работать и почему.</p><p>Неиспользуемый код удаляют, устаревший стек обновляют, а систему при необходимости переносят на новую платформу без изменения архитектуры. Отдельные модули рефакторят, заменяют готовым продуктом или переписывают по частям.</p><p>Обычно в одном проекте сочетаются сразу несколько стратегий. Так систему меняют постепенно, продолжают выпускать новые функции и проверяют результат после каждого этапа.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработка TypeScript-библиотеки для построения реактивных графов распространения и обработки данных</title>
      <link>https://tproger.ru/articles/razrabotka-typescript-biblioteki-dlya-postroeniya-reaktivnyh-grafo</link>
      <comments>https://tproger.ru/articles/razrabotka-typescript-biblioteki-dlya-postroeniya-reaktivnyh-grafo?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Сморен Фрилайт]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/razrabotka-typescript-biblioteki-dlya-postroeniya-reaktivnyh-grafo</guid>
      <description><![CDATA[<p>Как построить гибкий реактивный граф обработки данных, где узлы изолированы друг от друга, а связи между ними строятся автоматически на основе их возможностей? Рассказываю о разработке Transferum — легковесной TypeScript-библиотеки для реактивных потоков. Внутри: разбор системы вычислимых типов для проверки контрактов в compile-time, управление маршрутизацией данных в графе и примеры построения динамических пайплайнов для IoT-датчиков и панелей управления.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/razrabotka-typescript-biblioteki-dlya-postroeniya-reaktivnyh-grafo">Разработка TypeScript-библиотеки для построения реактивных графов распространения и обработки данных</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Функциональное программирование]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Реактивное программирование]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 23 Jul 2026 15:14:02 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Предыстория</h2><p>Все началось с рабочей задачи по реализации весьма специфичной web-панели мониторинга и управления различным оборудованием, в которой нужно было получать данные и отправлять команды по разнообразным сценариям (периодический опрос, подписка на websocket-события, https-запросы и т.д.), а также выводить состояние и графики в реальном времени, динамически комбинируя различные источники данных в разных виджетах.</p><p>Кроме того, у панели было предусмотрено несколько настраиваемых режимов работы, которые переключались по запросу пользователя, что требовало массового управления маршрутами потоков данных внутри системы.</p><p>В первой итерации с использованием RxJS получилось много неструктурированного и сложно поддерживаемого кода, так как архитектура на RxJS вынуждала императивно описывать перестроение топологии при изменении правил маршрутизации «на лету». В нашем специфичном кейсе с динамическими виджетами это приводило к сайд-эффектам и сложностям в отладке. Кроме того, местами размывалась строгая типизация и мы лишались compile-time гарантий. Так появилась идея разработать собственное решение на основе альтернативной концепции — модели графа потоков данных.</p><p>В результате получившаяся модель продемонстрировала предсказуемое поведение и низкую связность компонентов, а кодовая база сократилась на ~30% и приобрела более декларативный вид. Убедившись в эффективности решения, мы решили оформить его в виде отдельной библиотеки с открытым исходным кодом — <b>Transferum</b>.</p><h2>И что, получилась просто еще одна реактивная библиотека?</h2><p>Не совсем. Классические FRP-библиотеки (RxJS, Bacon, Most) построены вокруг единственного примитива (Observable<i></i>). Transferum же основан на композиции различных типов узлов с явно определенным поведением.</p><p>Каждый узел в графе потоков распространения данных явно декларирует свои способности: может ли он принимать данные через push, отдавать через pull, распространять полученный сигнал подписчикам, опрашивать источник, фильтровать, блокировать поток и т.д.</p><p>Объявленные узлом возможности являются одновременно <b>флагами для использования в runtime</b> и <b>compile-time гарантиями</b> наличия соответствующих методов, определяющих его поведение.</p><p><b>Ключевая идея:</b> поведение системы описывается как композиция независимых возможностей, которые одновременно определяют тип, реализацию и правила взаимодействия.</p><h2>Transferum предоставляет четыре слоя абстракции:</h2><ol><li>Трансферы — узлы графа (каналы, поллеры, мапперы, буферы, разветвители и концентраторы, реализации debounce, throttle, switchMap и т.д.).</li><li>Мосты — ребра графа — управляемые вентили между узлами с динамической маршрутизацией и гейтингом.</li><li>Операторы — stateless-трансформаторы и фильтры данных (используются трансферами, отвечающими за конвертацию данных).</li><li>Билдеры — fluent-конструкторы композитных трансферов из цепочек трансферов-примитивов.</li></ol><p>Концептуальная и архитектурная основа — <b>capability flags system</b>. Каждый трансфер реализует CommunicationContractInterface<i></i> — набор булевых флагов, определяющих его возможности.</p><p>Флаги isPushable, isPullable, isSubscribable, isGate и другие — это не просто свойства объекта. Это метаданные, которые:</p><ul><li>Определяют TypeScript-интерфейс трансфера на этапе компиляции.</li><li>Управляют стратегией связывания с другими трансферами в рантайме (с помощью функции linkTransfers()).</li><li>Обеспечивают совместимость в билдерах без приведений типов.</li></ul><p>Один набор флагов — три потребителя. Это единый источник истины для всей системы.</p><p>Когда флаг равен true, соответствующий метод входит в TypeScript-интерфейс трансфера. Это позволяет предоставлять трансфер пользователю вот так:</p><p>Или вот так:</p><p>Именно в таком формате типов фабрики в библиотеке возвращают трансферы. Например:</p><p>Эта <i>«магия»</i> работает в compile-time благодаря несколько замысловатой системе вычислимых типов:</p><h2>Архитектурные инварианты</h2><h2>1. Трансферы не знают своих соседей</h2><p>Трансфер определяет своё поведение (push, pull, subscribe и др.), но никогда не ссылается и не проверяет класс другого трансфера. Он не знает, что является upstream или downstream — лишь выполняет свой контракт. Пользователь может создать свой трансфер, объявить и реализовать его возможности — и он органично и бесшовно впишется в экосистему.</p><h2>2. Мосты не знают конкретных реализаций</h2><p>Мост инспектирует capability flags, а не имена классов. Нет цепочки instanceof, нет переключения по имени класса. Любой output-трансфер может быть соединен с любым input-трансфером — при условии совместимости их флагов, о чем мы поговорим чуть ниже. Это применимо и к тем узлам, которые еще не существуют и будут созданы пользователем.</p><h2>3. Значение undefined никогда не распространяется</h2><p>В Transferum undefined означает «нет данных», а не «пустое значение». Оно подавляется на уровне внутренней реализации менеджера подписок — подписчики никогда не уведомляются с undefined. При этом для явных маркеров пустых значений можно использовать null. <i>Мы сознательно пошли на этот компромисс, чтобы избежать runtime-оверхеда и сохранить нативную скорость работы на плотных потоках данных.</i></p><h2>Связывание трансферов</h2><p>Функция linkTransfers(lhs, rhs) соединяет output-трансфер (lhs) с input-трансфером (rhs) с автоматическим выбором стратегии связывания на основе возможностей этих трансферов:</p><ul><li>isSubscribable → isPushable (реактивная подписка);</li><li>isPullable → isPollingProxy (активный опрос);</li><li>isSubscribable → isAsyncPushable (реактивная подписка + асинхронный push);</li><li>isAsyncPullable → isAsyncPollingProxy (активный асинхронный опрос асинхронного pull-источника);</li><li>isPullable → isAsyncPollingProxy (активный асинхронный опрос синхронного pull-источника).</li></ul><p><b>Protocol-oriented design:</b> механизм не спрашивает <b>«какой это класс?»</b> — он выясняет, <b>какие у него есть возможности</b>. Любая пара трансферов с совместимыми возможностями является <b>linkable</b>. Добавление нового класса трансфера требует только объявления его флагов и реализации соответствующих методов — как связать его с другим трансфером, связующий алгоритм разберется сам.</p><h2>Sync и async в одной экосистеме</h2><p>Синхронные и асинхронные трансферы сосуществуют и могут быть связаны между собой. linkTransfers() предпочитает sync-связывание, когда это возможно, а async-стратегии применяет только когда sync неприменим. Нет отдельного «асинхронного мира».</p><h2>Поддержка backpressure</h2><p>Ряд асинхронных трансферов (AsyncSinkTransfer, AsyncWriteTransfer, AsyncConvertTransfer, AsyncConditionTransfer) поддерживают необязательные поля в конфигурации: maxConcurrency, bufferSize и onBufferOverflow — для ограничения параллельных async-операций, очереди избыточных данных и graceful-обработки переполнения. По умолчанию — неограниченная обработка, без буферизации.</p><h2>Локальная обработка ошибок</h2><p>Transferum использует единую модель обработки ошибок для всех трансферов. Каждый трансфер, который может столкнуться с runtime-ошибкой, принимает опциональный onError-хэндлер в своей конфигурации.</p><h2>А теперь — к примерам использования</h2><p>Вот так можно просто и декларативно описать опрос и агрегирование данных из нескольких источников:</p><p>А вот как можно организовать динамический роутинг:</p><p>Пример организации игровой механики:</p><h2>Когда имеет смысл попробовать Transferum</h2><p>Библиотека подойдет для:</p><ul><li>TypeScript-first проектов — благодаря максимально строгой типизации и compile-time вычислению доступных методов любого трансфера на основе объявленных у него флагов возможностей.</li><li>Работы с pull-based источниками данных — polling API, датчиков, хранилищ с PollingProxy.</li><li>Смешанных sync/async пайплайнов — в единой модели без ручного преобразования.</li><li>Явного flow control — gates, bridges, selectors для runtime-маршрутизации.</li><li>Game development / IoT — frame-aligned tickers, idle polling, sensor aggregation.</li><li>Устойчивой обработки ошибок — локальная, non-fatal обработка: одна стадия не убивает пайплайн при условии переданного в конфиге обработчика ошибок, ничего не подавляется молча.</li></ul><h2>Результаты и планы</h2><ul><li>Библиотека уже используется в двух наших внутренних проектах и показывает свою эффективность. Код доступен на GitHub под лицензией MIT, библиотека не имеет внешних зависимостей и поставляется с подробной документацией (README + API Reference).</li><li>В одном из проектов граф состоит из ~80 узлов и стабильно обрабатывает несколько сотен событий в секунду без деградации. На основе этих данных в том числе рендерится 3D-сцена в Babylon.js со стабильным фреймрейтом ~60 FPS без микрофризов.</li><li>Тесты библиотеки покрывают не только отдельные трансферы, но и поведение системы в динамике: переподключение мостов, обработку ошибок в длинных асинхронных цепочках, а также разнообразные граничные случаи. Покрытие — 100%.</li><li>В дальнейшем планируем реализовать хуки и утилиты для более удобного и нативного использования Transferum с Vue и React. Если они окажутся в достаточной мере переиспользуемыми, оформим в отдельные пакеты-адаптеры.</li></ul><p>Буду рад, если вы заглянете в <a href="https://github.com/Smoren/transferum-ts" rel="noopener noreferrer nofollow">репозиторий</a>, попробуете библиотеку в деле и поделитесь замечаниями — обратная связь поможет сделать Transferum лучше.</p><p>P. S. Если вы сталкивались с похожими задачами и решили их как-то иначе — буду рад прочитать о вашем опыте в комментариях.</p>]]></content:encoded>
    </item>
    <item>
      <title>Метаклассы в Python: как работает фабрика классов</title>
      <link>https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov</link>
      <comments>https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov</guid>
      <description><![CDATA[<p>Разбираем, что такое метаклассы в Python, как type создаёт классы и когда стоит писать свой метакласс. Примеры кода и практические рекомендации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov">Метаклассы в Python: как работает фабрика классов</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Объектно-ориентированное программирование]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 09 Jul 2026 12:12:32 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если класс в Python — это шаблон, по которому создаются объекты, то метакласс — это шаблон, по которому создаются сами классы. Идея звучит рекурсивно, но именно в ней скрывается ответ на вопрос, почему в Python всё — объект: даже определение класса можно собрать, изменить или зарегистрировать программно.</p><p>В этой статье разберём, как внутри устроены метаклассы, зачем нужен встроенный type с тремя аргументами и когда стоит писать свой метакласс, а когда — обойтись наследованием или декоратором.</p><h2>Класс — тоже объект</h2><p>В Python 3 любой класс является экземпляром метакласса. По умолчанию этим метаклассом выступает type. Поэтому запись class Foo: pass интерпретатор превращает примерно в такой вызов:</p><p>С точки зрения языка разницы почти нет: и в том, и в другом случае получается объект класса Foo, у которого есть атрибуты, методы и собственный тип. Проверить это можно прямо в интерпретаторе:</p><p>type сам является экземпляром себя — это одна из тех особенностей Python, которые сначала удивляют, а потом помогают понять, что классы и объекты в языке живут по одним правилам.</p><p>Метакласс — это класс, экземплярами которого являются другие классы.</p><p>В Python 3 по умолчанию метаклассом любого класса является type.</p><p>Вызов type(name, bases, dict) создаёт класс динамически.</p><p>Собственный метакласс наследуется от type и переопределяет __new__ или __init__.</p><p>Чаще всего задачу решают наследованием или декоратором класса — метакласс нужен редко.</p><h2>type — не только функция проверки типа</h2><p>Всем знаком вызов type(x), который возвращает тип объекта. Но если передать type три аргумента, он превращается в фабрику классов:</p><ul><li>первый аргумент — имя класса (становится __name__);</li><li>второй — кортеж базовых классов (становится __bases__);</li><li>третий — словарь пространства имён (становится __dict__).</li></ul><p>Этот механизм лежит в основе любого определения класса. Когда Python видит ключевое слово class, он сначала собирает тело класса в словарь, а затем вызывает метакласс, чтобы создать сам класс.</p><p>Пример — класс с методом, собранный вручную:</p><p>Такой код редко пишут в production, но он наглядно показывает, что класс — это всего лишь объект, созданный вызовом фабрики. А значит, эту фабрику можно заменить на свою.</p><h2>Как написать свой метакласс</h2><p>Чтобы изменить процесс создания класса, наследуемся от type и переопределяем __new__. Этот метод отвечает за создание самого класса, поэтому в нём можно добавить общие атрибуты, проверить имя или зарегистрировать класс в каталоге.</p><p>Здесь AutoAttrMeta автоматически добавляет атрибут version каждому классу, который её использует. То же самое можно сделать через наследование, но метакласс действует на этапе создания класса, а не при вызове методов.</p><p>Помимо __new__, часто переопределяют __init__ метакласса: он получает уже созданный класс и может его донастроить. А __call__ контролирует создание экземпляров класса — именно он вызывает __new__ и __init__ объекта.</p><h2>А точно нужен метакласс?</h2><p>Тим Петерс, автор «Дзена Python», как-то сказал, что метаклассы — это магия, которую 99% разработчиков никогда не понадобится. Если вы сомневаетесь, нужны ли они вам, — скорее всего, не нужны.</p><blockquote>Метаклассы — это более глубокая магия, чем та, о которой 99% пользователей должны беспокоиться. Если вы сомневаетесь, нужны ли они вам, значит, не нужны.</blockquote><p>Для сравнения — три способа дать классам общий атрибут:</p><h3>Наследование</h3><h3>Декоратор класса</h3><h3>Метакласс</h3><p>Наследование и декоратор читаются проще и не лезут в механику создания класса. Метакласс выигрывает, когда поведение должно быть неизбежным для всех наследников или когда логика завязана на сам процесс построения класса.</p><p><b>Когда метакласс действительно уместен:</b><br />— автоматическая регистрация подклассов в плагиновой системе;<br />— валидация объявлений полей (например, ORM проверяют типы атрибутов);<br />— принудительный единый API для большого семейства классов;<br />— реализация паттернов вроде Singleton на уровне класса.</p><h2>Практический пример: автоматическая регистрация плагинов</h2><p>Представьте, что вы пишете расширяемую систему: каждый новый адаптер должен попадать в общий реестр. Метакласс может добавлять класс в реестр сразу при определении, без ручного вызова регистрации.</p><p>Такой подход удобен, потому что автор плагина просто объявляет класс, а реестр обновляется сам. В реальных фреймворках — например, в старых версиях Django — похожая идея используется для построения моделей данных.</p><h2>Выводы</h2><p>Метаклассы — не ежедневный инструмент, но понимание их работы делает код Python прозрачнее. Вы начинаете видеть, что class — это тоже объект, а type — всего лишь фабрика, которую при желании можно заменить.</p><p>Главное правило: если задачу решает наследование или декоратор класса, не тащите метакласс. А если без контроля за созданием класса никуда — смело берите type и наследуйтесь от него.</p><p>Источник и дополнительное чтение: <a href="https://realpython.com/python-metaclasses/">Python Metaclasses</a> на Real Python.</p>]]></content:encoded>
    </item>
    <item>
      <title>Выделенные команды, аутсорс или инхаус: как считать реальный TCO</title>
      <link>https://tproger.ru/articles/vydelennye-komandy-autsors-ili-inhaus-kak-schitat-realnyj-tco</link>
      <comments>https://tproger.ru/articles/vydelennye-komandy-autsors-ili-inhaus-kak-schitat-realnyj-tco?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/vydelennye-komandy-autsors-ili-inhaus-kak-schitat-realnyj-tco</guid>
      <description><![CDATA[<p>Сравниваем TCO инхауса, аутсорса и выделенных команд: скрытые расходы, формулы расчёта и чек-лист для выбора модели разработки под ваш проект.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/vydelennye-komandy-autsors-ili-inhaus-kak-schitat-realnyj-tco">Выделенные команды, аутсорс или инхаус: как считать реальный TCO</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Управление проектами]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Jun 2026 11:38:49 GMT</pubDate>
      <content:encoded><![CDATA[<p>В инхаусе к зарплате быстро добавляются найм, налоги, рабочее место, онбординг, отпуск, больничные и время техлида. В аутсорсе часть этих расходов уже зашита в цену подрядчика. В выделенных командах вы платите за специалиста у провайдера, но управление задачами, ревью и качество результата остаются внутри вашей команды.</p><p>Поэтому считать стоит не “сколько стоит разработчик”, а “сколько стоит довести задачу до продакшена”. Для этого и нужен TCO (total cost of ownership): полная стоимость владения командой, процессом или внешним контуром разработки.</p><p>В этой статье Centicore Group считает реальные расходы по каждой модели и разбирает, при каких сценариях каждая из них выигрывает.</p><h2>Почему ставка разработчика ничего не объясняет</h2><p>Представим две команды. У первой ставка ниже, поэтому в смете она выглядит выгоднее. Но задачи двигаются медленно, баги возвращаются после фиксов, а техлид пропускает встречи, где нужно принимать технические решения.</p><p>У второй команды ставка выше. Зато есть понятный бэклог, документация, регулярное ревью и быстрые ответы по спорным вопросам. На этапе закупки первая команда может победить по цене, но в реальной разработке заказчик заплатит за задержки, переделки и лишнее управление.</p><p>TCO появляется как раз между “купили часы разработчиков” и “получили работающую фичу в продакшене”. В расчет попадают дополнительные расходы:</p><ul><li>запуск работы: найм, поиск подрядчика, собеседования, согласование договора, доступы, онбординг;</li><li>управление: постановка задач, ревью, синки, планирование, контроль сроков, приемка результата;</li><li>риски: замена специалиста, простой, передача знаний, ошибки в требованиях, технический долг.</li></ul><p><b>Для инхауса</b> ставка часто выглядит ниже, потому что компания смотрит на зарплату. Затем сверху приезжают налоги, оборудование, лицензии, HR, отпуск, больничные и время руководителя.</p><p><b>В аутсорсе</b> цена обычно выше прямой себестоимости команды. Подрядчик закладывает менеджмент, риски, тестирование, простой людей между проектами и свою маржу. Это нормально, если вы покупаете предсказуемый результат и снимаете часть операционной нагрузки.</p><p><b>В выделенной команде</b> легко попасть в ловушку “возьмём человека и ускоримся”. Ускорение появится, если внутри уже есть техлид, код-ревью и понятный процесс.</p><p>Базовая формула такая:</p><p>TCO разработки = прямые расходы + управление + запуск + простои + риски + передача знаний.</p><p>Где:</p><ul><li>Прямые расходы — показывают, сколько стоит доступ к людям и инструментам.</li><li>Управление показывает, сколько времени ваша команда тратит на то, чтобы эти люди двигались в нужную сторону.</li><li>Запуск и передача знаний показывают, сколько стоит ввести человека или подрядчика в контекст.</li><li>Простои и риски напоминают, что помимо основного плана есть дополнительные затраты, которые нужно закладывать изначально.</li></ul><p>На практике скрытых статей расходов может быть больше — всё зависит от масштаба команды, зрелости процессов и специфики проекта, поэтому добавляйте в формулу данные, которые считаете необходимыми для полного понимания предстоящих расходов.</p><h2>Как считать TCO инхауса</h2><p>В инхаусе легко начать расчёт с зарплаты разработчика и решить, что основная сумма уже понятна. На деле штатный специалист стоит компании дороже оффера. К зарплате добавляются налоги, техника, лицензии, рабочее место, HR, адаптация, обучение, отпуск, больничные и время руководителей.</p><p>Формула может быть такой:</p><p>TCO инхауса = зарплата + налоги + инфраструктура + найм + онбординг + управление + простой + риск замены</p><ul><li>Зарплата и налоги можно посчитать сразу. Остальное часто всплывает позже. Разработчику нужны ноутбук, монитор, доступы, IDE, таск-трекер, облачные сервисы, тестовые стенды и корпоративные инструменты.</li><li>Найм тоже входит в TCO. Вакансию нужно описать, кандидатов найти, провести скрининг, техническое интервью, тестовое задание и согласование оффера.</li><li>После выхода человека начинается онбординг. Разработчик разбирается в кодовой базе, архитектуре, локальном окружении, правилах ревью и деплоя. Первые недели он часто требует больше внимания, чем отдаёт команде пользы. Это нормальная часть штатной разработки, её просто нужно считать заранее.</li><li>Отдельная статья расходов — текучка. Когда разработчик уходит, компания теряет часть контекста. Потом нужно снова искать человека, вводить его в проект и ждать, пока он выйдет на нормальную скорость.</li></ul><p>И это также упрощённая формула. В расширенной версии формулы TCO инхауса добавляются потери производительности оставшейся команды, стоимость передачи знаний и риск того, что ушедший специалист унёс с собой критически важный контекст.</p><p>Инхаус окупается, когда разработка завязана на сложную бизнес-логику, безопасность, внутренние интеграции или долгую архитектурную стратегию. Для короткого проекта инхаус получается слишком дорогим. Если задача нужна на несколько месяцев, в TCO попадает вся стоимость запуска штатной команды ради ограниченного объёма работ.</p><h2>Как считать TCO аутсорса</h2><p>В аутсорсе компания платит за внешний контур разработки: команду, процесс, менеджмент и результат по договору. Поэтому TCO здесь считают от стоимости проекта.</p><p>Формула может быть такой:</p><p>TCO аутсорса = стоимость договора + подготовка требований + управление со стороны заказчика + приёмка + изменения цели + передача результата</p><ul><li>Стоимость договора обычно включает работу команды, PM, тестирование, инфраструктуру подрядчика и его маржу. Маржа в этой модели нормальна: подрядчик держит команду, управляет загрузкой, закрывает внутренние риски и отвечает за процесс на своей стороне.</li><li>Главная точка роста стоимости — требования. Когда финальная цель понятная, подрядчик быстрее оценивает задачу, планирует этапы и показывает результат.</li><li>Заказчику всё равно нужно управлять проектом со своей стороны. Подрядчик не знает продуктовый контекст по умолчанию. Ему нужны ответы на вопросы, доступы, обратная связь и приёмка промежуточных результатов.</li><li>В TCO аутсорса стоит сразу закладывать передачу результата. В договоре нужно зафиксировать права на код. Документация должна позволять поддерживать проект другой команде. Деплой, окружения и API тоже лучше описать до финальной приёмки.</li></ul><p>В более сложных ситуациях формула расширяется: добавляются стоимость аудита переданного кода, расходы на адаптацию под внутренние стандарты и время на то, чтобы новая команда вообще разобралась с проектом.</p><p>Аутсорс хорошо подходит для MVP, отдельных сервисов, интеграций, миграций и задач с понятными границами. Если продукт часто меняется, процесс и договор должны поддерживать итерации, иначе каждая новая вводная будет разгонять стоимость.</p><h2>Как считать TCO выделенных команд</h2><p>Модель с выделенными командами выглядит просто: берём специалиста или готовую команду у подрядчика, подключаем к своей команде, платим за их время. Эта модель работает лучше там, где внутри уже есть техническое управление. Внешнему разработчику нужны задачи, контекст, ревью и человек, который принимает технические решения.</p><p>Формула может быть такой:</p><p>TCO выделенных команд = ставка специалиста + подбор + онбординг + управление + ревью + коммуникация + риск замены</p><ul><li>Ставка специалиста даёт доступ к человеку, а готовый результат всё равно собирает ваша команда. Подрядчик может помочь с подбором, оформлением, заменой и административной частью. Заказчик отвечает за ежедневную работу специалиста.</li><li>Самая важная статья расходов здесь — время техлида. Он проводит техническое интервью, вводит человека в проект, объясняет архитектуру и проверяет решения.</li><li>Онбординг в аутстаффинге обычно короче, чем в инхаусе: компания не проходит полный цикл найма и оформления, но контекст проекта всё равно нужно передать.</li><li>В TCO нужно заложить коммуникацию с провайдером. Если специалист заболел, не подошёл по уровню или проекту нужна замена, порядок действий должен быть понятен заранее.</li></ul><p>Но помните, что это базовые составляющие. У некоторых компаний сюда добавляются расходы на юридическое сопровождение договора с провайдером, согласование NDA и внутренние процедуры безопасности при подключении внешних специалистов к инфраструктуре.</p><h2>Где здесь место внешней команды</h2><p>Внешнюю команду нужно подключать к конкретной зоне расходов. Если внутри есть техлид, бэклог и процесс ревью, можно усилить команду через <b>выделенные команды.</b> Если нужно закрыть отдельный модуль, интеграцию или сервис, логичнее смотреть в сторону <b>заказной разработки.</b> Если проект пока держится на общих формулировках, полезно сначала разобрать требования, архитектуру и объём работ.</p><p>Здесь помогает простой вопрос: что сейчас нужно купить. Часы специалистов, готовый контур разработки или помощь с постановкой задачи. Centicore Group работает с выделенными командами IT-специалистов, заказной разработкой и IT-консалтингом. Поэтому формат можно подбирать под ситуацию: усилить свою команду, передать отдельную часть разработки внешней команде или начать с анализа требований и архитектуры.</p><p><a href="https://centicore.ru/services/">Посмотреть услуги Centicore Group</a></p><h2>Чек-лист перед выбором модели</h2><p>Перед выбором модели проверьте три вещи: управление, срок проекта и требования.</p><ol><li>Сначала — управление. Если внутри есть CTO, техлид или сильный PM с техническим бэкграундом, можно рассматривать аутстаффинг и гибридную модель. Если управленца нет, аутстаффинг быстро станет проблемой. В этом случае безопаснее смотреть на аутсорс, где управление разработкой берёт на себя подрядчик.</li><li>Дальше — срок проекта. Для задачи на несколько месяцев инхаус часто слишком дорогой: найм, адаптация и настройка процессов могут занять больше времени, чем сама разработка. Для продукта на долгосрок, наоборот, стоит заранее думать о своём техническом ядре.</li><li>Третий пункт — требования. Чем понятнее сценарии, интеграции, ограничения и критерии готовности, тем проще считать TCO аутсорса. Если требования ещё меняются, в бюджет нужно сразу закладывать аналитику, дополнительные итерации и переприёмку.</li></ol><p>Перед решением ответьте на несколько вопросов:</p><ul><li>кто владеет архитектурой и техническими решениями;</li><li>кто принимает код, документацию и деплой;</li><li>что будет, если ключевой специалист выпадет из проекта.</li></ul><p>После этого обычно становится видно, что именно нужно проекту: штатная команда, внешний подрядчик, аутстафф-специалист или гибрид.</p><h2>Итого</h2><p>TCO показывает реальную стоимость результата, а не цену одного разработчика. В расчёте должны быть управление, запуск, простои, риски, передача знаний и поддержка после релиза.</p><ul><li>Инхаус подходит для долгих продуктов, где важны контроль, архитектура и накопление экспертизы внутри команды.</li><li>Аутсорс удобен для MVP, интеграций, миграций и отдельных сервисов с понятными границами. Здесь важно заранее считать требования, приёмку, документацию и передачу результата.</li><li>Аутстаффинг помогает быстро усилить команду, если внутри уже есть техлид, бэклог, ревью и нормальный процесс разработки.</li><li>Гибридная модель нужна, когда продукт проходит разные этапы. На старте можно подключить внешнюю команду, после проверки гипотезы собрать своё ядро, а пиковую нагрузку закрывать аутсорсом или аутстаффингом.</li></ul><p>Капитанский вывод, но полезный: считайте не ставку разработчика, а стоимость релизов.</p>]]></content:encoded>
    </item>
    <item>
      <title>Netflix построила Service Topology: живая карта микросервисов</title>
      <link>https://tproger.ru/articles/netflix-postroila-zhivuyu-kartu-servisov-kak-ustroena-service-top</link>
      <comments>https://tproger.ru/articles/netflix-postroila-zhivuyu-kartu-servisov-kak-ustroena-service-top?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/netflix-postroila-zhivuyu-kartu-servisov-kak-ustroena-service-top</guid>
      <description><![CDATA[<p>Как Netflix объединяет eBPF, IPC-метрики и tracing в единую карту зависимостей. Разбираем, почему статические схемы устарели и что перенести в свою систему.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/netflix-postroila-zhivuyu-kartu-servisov-kak-ustroena-service-top">Netflix построила Service Topology: живая карта микросервисов</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Графы]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 19 Jun 2026 07:47:47 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы хоть раз отлаживали микросервис ночью, знаете главный вопрос: это мой сервис сломался или его уронило что-то выше по течению? В системе из тысяч сервисов ответ приходится искать по кускам — метрики, логи и трейсы показывают симптомы, но не дают единой карту зависимостей. Netflix столкнулась с этой же болью и построила инструмент под названием Service Topology, который рисует живую карту зависимостей в реальном времени.</p><p>Service Topology — это не статическая схема из вики, а динамическая карта связей между сервисами. Она обновляется по мере того, как меняется трафик, появляются новые зависимости или старые исчезают. Карта показывает не только «кто с кем говорит», но и контекст: уровень доступности, бизнес-домен, владельца и текущее состояние здоровья.</p><p>Если тема микросервисов для вас новая, начните с базового разбора <a href="https://tproger.ru/articles/chto-takoe-mikroservisy--arhitektura--plyusy-i-minusy--primery">«Что такое микросервисы»</a>, а если выбираете архитектуру для проекта — сравните варианты в материале <a href="https://tproger.ru/articles/monolit-ili-mikroservisy--kak-vybrat-arhitekturu-dlya-novogo-proekta">«Монолит или микросервисы»</a>. Авторский разбор внутреннего устройства Netflix читайте в статье <a href="https://tproger.ru/articles/razrabotchik-izuchal-sistemu-rekomendacij-netflix-mesyacami--vot-chto-skryvaetsya-vnutri">«Разработчик изучал систему рекомендаций Netflix месяцами»</a>.</p><ul><li>Netflix объединила три источника данных: eBPF-сетевые потоки, IPC-метрики и распределённые трейсы.</li><li>Каждый источник строит свой граф; общий вид получается параллельным слиянием слоёв.</li><li>Карта обновляется почти в реальном времени и отвечает на запросы быстрее секунды.</li><li>Инженеры используют её для поиска причин сбоев, оценки зоны поражения и планирования изменений.</li><li>Подход можно перенести в любую распределённую систему, где нужно понимать зависимости.</li></ul><h2>Почему обычной наблюдаемости мало</h2><p>Традиционные инструменты наблюдаемости показывают фрагменты картины. Метрики говорят, что что-то болит. Логи рассказывают, что конкретно произошло в одном сервисе. Трейсы прослеживают путь отдельного запроса. Но ни один из этих сигналов не показывает полную топологию зависимостей — ту самую основу, на которой держится распределённая архитектуры.</p><p>Инженеру в три часа ночи приходится мысленно склеивать данные из разных источников. Это медленно, чревато ошибками и добавляет стресса. Netflix проанализировала тысячи обращений в поддержку за четыре года и увидела повторяющиеся вопросы: кто мои upstream и downstream, что упадёт вместе со мной, почему сервис отображается в панели мониторинга как Unknown (неизвестный сервис). Ответы на них требовали единого представления о зависимостях.</p><h2>Три источника данных Service Topology</h2><p>Главный вывод Netflix: ни один источник не рассказывает всю историю. Поэтому система строит три независимых графа и объединяет их по запросу.</p><h3>Сетевые потоки eBPF</h3><p>На сетевом уровне Netflix собирает потоки ядра через eBPF. Это даёт полноту: сюда попадают все соединения, независимо от того, инструментирован сервис или нет. Слой показывает связи между кластерами и приложениями такими, какими они есть на самом деле. Недостаток — не хватает прикладного контекста: видно, что сервис А достучался до IP-адреса сервиса Б, но неизвестно, какой конкретно endpoint вызывался.</p><h3>IPC-метрики</h3><p>На прикладном уровне собираются метрики межпроцессного взаимодействия. Когда сервис обращается к другому через gRPC, GraphQL или REST, он фиксирует endpoint, ошибки, задержки и протокол. Этот слой даёт детали, которых нет у сетевого: какой именно путь вызывается и с какой вероятностью ошибки. Ограничение очевидно: если сервис не отправляет метрики, его вызовов здесь не будет.</p><h3>Распределённые трейсы</h3><p>Третий слой — трейсинг запросов от начала до конца. Он показывает не «может ли сервис А позвонить сервису Б», а «звонил ли он в рамках этого конкретного пользовательского запроса». Это помогает увидеть поведение во время выполнения, ветвления, фича-флаги и редкие пути. Поскольку трейсы собирают выборочно, редкие сценарии могут не попасть в агрегированный вид.</p><p>Когда инженер запрашивает общую картину, система обходит все три графа параллельно и сливает результаты. Сеть обеспечивает полноту, IPC добавляет контекст, трейсы показывают реальное поведение. Каждый источник компенсирует слабости остальных.</p><h2>Как собирают Service Topology</h2><h3>Приём и обработка потоков</h3><p>За кулисами работает конвейер, который держит миллионы событий в секунду. Потоки сетевых логов читаются из Kafka в нескольких регионах AWS. Для обработки Netflix использует Apache Pekko Streams — форк Akka, который разбивает нагрузку по группам автомасштабирования и сам управляет обратным давлением.</p><h3>Восстановление прямых связей</h3><p>Сетевой лог показывает отдельные сетевые прыжки: например, приложение → балансировщик → приложение или приложение → NAT-шлюз → приложение. Чтобы получить настоящие связи, запускается трёхступенчатая агрегация. Первая стадия забирает сырые записи, вторая распознаёт посредников и восстанавливает прямые пути между приложениями, третья финализирует агрегаты и добавляет статус здоровья. Такой градуированный подход раскидывает нагрузку и не даёт горячим узлам уронить весь конвейер.</p><p>Готовая топология хранится в графовой базе Netflix — абстракции поверх распределённого хранилища «ключ — значение». Она заточена под быстрый обход графа в несколько хопов. Поверх базы — gRPC-API с фильтрами по уровню доступности и домену, постраничным выводом больших выборок и ответом менее чем за секунду.</p><h3>Хранение и API</h3><p>Отдельно стоит возможность «путешествия во времени». Система не хранит каждый момент отдельно, а накапливает данные в скользящих окнах. Это позволяет спросить «как выглядела топология вчера в полночь» без взрыва объёмов хранения.</p><h2>Что даёт инженерам</h2><p>Интерфейс и API дают инженерам несколько рабочих сценариев — от ручного расследования до автоматических проверок.</p><ul><li>Видеть upstream и downstream для любого сервиса с фильтрами по уровню доступности и домену.</li><li>Переключаться между единым видом и отдельными слоями: только сеть, только IPC или только трейсы.</li><li>Одним кликом переходить от узла топологии к логам, трейсам и детальным метрикам.</li><li>Оценивать зону поражения перед отключением сервиса на обслуживание и понимать, кого уведомлять.</li><li>Накладывать статус здоровья на граф и быстро понимать, локальная ли это проблема или каскадный сбой.</li><li>Обращаться к топологии программно — например, чтобы автоматически проверять классификацию доступности критичных сервисов.</li><li>Смотреть историю зависимостей и находить, что изменилось перед инцидентом.</li></ul><h2>Как перенести в свою систему</h2><p>Не каждая компания работает в масштабе Netflix, но логика подхода универсальна. Если у вас десятки сервисов, уже появляется эффект «тысячи кусочков пазла». Начать можно с малого: собрать список зависимостей из существующих источников и периодически сверять его с реальным трафиком.</p><p><b>Чек-лист для первых шагов:</b><br />1. Выберите один настоящий источник связей: логи балансировщика, агент на хосте или метрики вызовов.<br />2. Не пытайтесь сразу построить идеальную модель — начните с автоматически обнаруженных рёбер.<br />3. Добавьте контекст: владельца сервиса, уровень критичности и домен.<br />4. Сделайте карту доступной программно, а не только в интерфейсе — инцидент-боты и скрипты будут благодарны.<br />5. Проверяйте актуальность: в динамичной среде карты, устаревшие на несколько часов, быстро теряют ценность.</p><p>Для иллюстрации вот минимальный скрипт, который строит рёбра графа из логов балансировщика:</p><h2>Часто задаваемые вопросы</h2><h2>Выводы</h2><p>Service Topology — не просто красивая картинка для панели мониторинга. Это операционная основа, которая ускоряет расследования, снижает риск изменений и даёт автоматическим системам общую картину инфраструктуры. Netflix называет её knowledge graph foundation — фундаментом для интеллектуальной автоматизации, в том числе для автоматического поиска первопричины сбоев.</p><blockquote>Service topology provides the knowledge graph foundation that makes this kind of intelligent automation possible.</blockquote><p>Для российских команд, где инфраструктура тоже стремительно усложняется, главный урок в другом: не ждите идеальной полноты данных. Начните с одного реального источника, добавьте контекст, сделайте карту программно доступной — и она начнёт приносить пользу раньше, чем вы построите «полноценную» систему.</p><h2>Источники</h2><ul><li><a href="https://medium.com/netflix-techblog/from-silos-to-service-topology-why-netflix-built-a-real-time-service-map-0165ba13a7bc">From Silos to Service Topology: Why Netflix Built a Real-Time Service Map</a></li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Agentic SRE: как ИИ-агенты меняют практику надёжности</title>
      <link>https://tproger.ru/articles/agentic-sre-kak-ii-agenty-menyayut-praktiku-nadyozhnosti</link>
      <comments>https://tproger.ru/articles/agentic-sre-kak-ii-agenty-menyayut-praktiku-nadyozhnosti?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/agentic-sre-kak-ii-agenty-menyayut-praktiku-nadyozhnosti</guid>
      <description><![CDATA[<p>Разбираем, как агентный подход к SRE ускоряет диагностику инцидентов и сокращает рутину. Пять рабочих сценариев, стек инструментов и правила безопасности. Читайте и внедряйте.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/agentic-sre-kak-ii-agenty-menyayut-praktiku-nadyozhnosti">Agentic SRE: как ИИ-агенты меняют практику надёжности</a>»</p>]]></description>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 31 May 2026 07:22:48 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы дежурите по ночам и проводите значительное время инцидента в поисках контекста — есть способ существенно сократить этот этап. <b>Agentic SRE</b> (агентный подход к Site Reliability Engineering) — это эволюция практики надёжности, где ИИ-агенты берут на себя рутину наблюдения, диагностики и безопасного реагирования. Цель не в том, чтобы заменить инженера, а в том, чтобы дать ему сверхспособности в моменты, когда каждая секунда дорога.</p><h2>Что такое Agentic SRE</h2><p>Agentic SRE — это практика, в которой автономные ИИ-агенты помогают наблюдать за системами, анализировать телеметрию (метрики, логи, трейсы) и выполнять ограниченные операционные действия при заданных человеком guardrails (правил безопасности). Агент не заменяет SRE-инженера, а работает как цифровой напарник: собирает контекст, предлагает гипотезы и выполняет только те действия, которые одобрены политикой.</p><p>Ключевое отличие от классической автоматизации — способность к <b>рассуждению</b>. Традиционные скрипты реагируют на жёсткие триггеры («если CPU &gt; 90%, то перезапустить»). Агент же может связать всплеск ошибок с недавним развёртыванием, проверить корреляцию по трейсам и предложить конкретный план действий — всё до того, как инженер откроет ноутбук.</p><p>Agentic SRE — это ИИ-агенты, которые помогают инженерам наблюдать, диагностировать и реагировать на инциденты.</p><p>Основная цель — сократить рутину и ускорить принятие решений, а не заменить человека.</p><p>Ключевые сценарии: триаж алертов, инцидент-копилот, автоматический RCA, безопасное исправление, обучение на инцидентах.</p><p>Безопасность превыше всего: агенты работают с allowlists, approval gates и полным аудитом.</p><p>Российские команды могут строить стек на OpenTelemetry, Prometheus/Grafana, собственных LLM и PagerDuty/Opsgenie.</p><h2>Почему это важно именно сейчас</h2><p>Сегодняшние системы стали слишком распределёнными, шумными и динамичными, чтобы человек успевал за ними вручную. Микросервисы, Kubernetes, feature flags и канареечные развёртывания создали ландшафт, где одно изменение может затронуть десятки сервисов, а сигналы о проблемах разбросаны по десяткам дашбордов.</p><p>Инженеры тратят значительное время на корреляцию метрик, чтение логов, проверку недавних выкаток и охоту за контекстом — до того, как начинают решать проблему. Agentic SRE решает это, превращая сырые телеметрические данные в структурированный контекст и автоматизируя безопасные части цикла реагирования.</p><p>Особенно это критично ночью и в выходные, когда на дежурстве один человек, а давление предельно велико. Задачи, которые легко стандартизировать, но сложно выполнить идеально в 2 часа ночи, — идеальная ниша для агентов, которые умеют обобщать, коррелировать и рекомендовать действия согласно политике.</p><h2>Из чего строится стек</h2><p>Практичная архитектура агентного SRE собирается из нескольких слоёв. При этом важно понимать: ценность приходит не от выбора модели, а от качества контекста, жёстких ограничений и дисциплины исполнения.</p><ul><li>Телеметрия: OpenTelemetry — стандарт сбора телеметрии — для сбора логов, метрик и трейсов в едином формате.</li><li>Наблюдаемость: Prometheus, Grafana, Datadog, New Relic, Elastic или облачные системы — хранилище и визуализация.</li><li>Оркестрация: MCP-серверы (Model Context Protocol от Anthropic), внутренние API или системы рабочих процессов, которые предоставляют агенту безопасные инструменты.</li><li>Среда выполнения агента: LLM-фреймворки с function calling, планированием и работой с инструментами.</li><li>Рабочий процесс при инцидентах: PagerDuty, Opsgenie, Slack, Jira, ServiceNow — куда агент пишет отчёты и запрашивает подтверждения.</li><li>Safety Layer: RBAC, approval gates, audit logs, allowlists и пути отката — неотъемлемая часть, а не дополнение.</li></ul><p><b>Принцип безопасности:</b><br />Агент должен уметь делать только то, что мог бы сделать дежурный инженер после одобрения. Это делает систему практичной и снижает риск случайного ущерба.</p><h2>Пять сценариев, которые уже работают</h2><h3>1. Триаж алертов: от шума к сути за секунды</h3><p>Когда срабатывает алерт, агент собирает связанные трейсы, проверяет недавние развёртывания, находит соответствующие всплески в логах и формирует краткое резюме на русском языке: вероятная причина, уверенность, рекомендуемые действия. Это решает проблему «с чего начать?», которая сжигает драгоценные минуты в начале инцидента.</p><h3>2. Инцидент-копилот: второй мозг в канале</h3><p>Агент подключается к каналу реагирования (Slack, Teams) и выполняет роль «второго мозга»: строит таймлайн, подтягивает ссылки на дашборды, отслеживает гипотезы по мере их проверки. Когда в инциденте участвуют несколько инженеров, это предотвращает фрагментацию контекста и дублирование усилий.</p><h3>3. Автоматический черновик RCA (Root Cause Analysis)</h3><p>После инцидента агент сравнивает таймлайн с недавними изменениями (развёртывания, конфиги, фичер-флаги), находит корреляции и генерирует первый черновик отчёта RCA с ссылками на доказательства. Инженер остаётся владельцем выводов, но экономит значительное время на подготовку документации.</p><h3>4. Безопасное исправление под контролем</h3><p>Для хорошо изученных типов инцидентов агент может рекомендовать или выполнять низкорискованные действия: перезапуск упавшего экземпляра сервиса, масштабирование развёртывания, отключение сломанного фичер-флага. Но всё, что влияет на пользовательский трафик, требует одобрения человека.</p><h3>5. Обучение на инцидентах: от пожаротушения к профилактике</h3><p>Агент анализирует паттерны в разных инцидентах, выявляет повторяющиеся корневые причины и предлагает, где улучшить наблюдаемость или сократить рутину. Со временем это создаёт обратную связь: боль в продакшене превращается в инвестиции в платформу. Точные алерты, подробные трейсы, недостающие дашборды — всё это становится следствием системной работы, а не хаотичных пост-инцидентов.</p><h2>Guardrails: почему доверие важнее интеллекта</h2><p>Главный риск агентного SRE не в том, что агент слишком умен, а в том, что он может быть <b>слишком уверен в себе</b>. LLM способны генерировать правдоподобные, но неверные объяснения. Поэтому каждая рекомендация должна быть прослежена до реальной телеметрии, а каждое действие — ограничено явными правами.</p><ul><li>Action allowlists: агент может делать только то, что в белом списке.</li><li>Approval gates: рискованные изменения требуют подтверждения.</li><li>Полный audit log: каждое действие агента записывается.</li><li>Read-only mode на этапе внедрения: сначала наблюдаем, потом действуем.</li><li>Разграничение прав по сервисам и командам.</li><li>Human override на каждом шаге: человек всегда может взять управление.</li></ul><blockquote>Если вы воспринимаете агента как стажёра с феноменальной памятью, но без здравого смысла, вы спроектируете систему с гораздо лучшей защитой.</blockquote><h2>Выводы</h2><p>Agentic SRE — это не модный тренд, а логичный ответ на растущую сложность инфраструктуры. Реальная ценность не в полной автономии, а в ускорении понимания, безопасной автоматизации и качественной коллаборации человека и машины в критические моменты.</p><p>Если строить это правильно, результат — продвинутая практика надёжности: меньше бесполезной работы, короче инциденты и больше времени для инженеров на системные улучшения вместо повторяющейся рутины. Настоящая ценность агентного SRE — не в замене инженера, а в том, чтобы дать ему модель работы с гораздо большей эффективностью.</p><p>Источник: <a href="https://devops.com/agentic-sre-the-next-frontier-of-reliability/">Agentic SRE: The Next Frontier of Reliability — DevOps.com</a> (Neel Shah, 2026).</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработка B2B-продуктов: как построить отношения между пользователем и командой продукта</title>
      <link>https://tproger.ru/articles/razrabotka-b2b-produktov-kak-soblyusti-balans-mezhdu-komfortom-ko</link>
      <comments>https://tproger.ru/articles/razrabotka-b2b-produktov-kak-soblyusti-balans-mezhdu-komfortom-ko?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Наталья Буйлина]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/razrabotka-b2b-produktov-kak-soblyusti-balans-mezhdu-komfortom-ko</guid>
      <description><![CDATA[<p>О том, где в B2B-продуктах чаще всего возникают скрытые сложности и как найти баланс между потребностями бизнеса, пользователей и разработчиков.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/razrabotka-b2b-produktov-kak-soblyusti-balans-mezhdu-komfortom-ko">Разработка B2B-продуктов: как построить отношения между пользователем и командой продукта</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Low-code]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 21 May 2026 08:27:35 GMT</pubDate>
      <content:encoded><![CDATA[<p>В крупных B2B-продуктах удобство нельзя оценивать только по интерфейсу. За привычным экраном пользователя часто стоит сложная платформа, десятки взаимосвязанных компонентов и команды, которым нужно не только развивать продукт, но и поддерживать его стабильность в реальных клиентских сценариях. Поэтому при разработке корпоративных решений важно учитывать не только путь конечного пользователя, но и опыт внутренних команд — продуктовых групп, внедрения и эксплуатации.</p><p>О том, где в таких продуктах чаще всего возникают скрытые сложности и как найти баланс между потребностями бизнеса, пользователей и разработчиков, рассказала <b>Наталья Буйлина, лидер стрима ITSM платформы производства ПО «Сфера» (входит в ИТ-холдинг Т1).</b></p><p>Корень большинства проблем лежит глубже — в логике сценариев и архитектуре. Интерфейс лишь отражает то, насколько хорошо продуманы пользовательские пути. В продуктах, работающих внутри платформы, главная боль — скрытые зависимости от сервисов и сценарии, которые «ломаются» при любых изменениях в базовой инфраструктуре. При этом человек уверен, что работает с одним продуктом, хотя за ним стоят 5–10 компонентов.</p><p><b>Внутренние команды: пользователи, о которых забывают</b></p><p>Особенность B2B-сегмента в том, что каждый сервис рассчитан сразу на несколько категорий потребителей. Помимо конечных клиентов есть службы эксплуатации, специалисты по внедрению и продуктовые группы, встраивающие компоненты платформы в свои решения. Буйлина подчеркнула, что внутренним командам почти всегда приходится сложнее, хотя со стороны это и незаметно — трудности скрыты за фасадом процессов разработки.</p><p>Продуктовые группы быстро обнаруживают, что воспроизвести реальные клиентские сценарии непросто: часть логики находится в коде, часть — в конфигурации, а изолированная среда для тестирования может отсутствовать. В службах поддержки подход еще прагматичнее: когда процесс нужно восстановить здесь и сейчас, команда не ждет штатного решения, а правит данные в базе вручную или обходит стандартные процедуры. Если подобных обходных сценариев накапливается слишком много, это верный признак того, что платформа еще не полностью сформирована.</p><p><b>No-code и low-code: гибкость, которая может стать ловушкой</b></p><p>Отдельного внимания заслуживает работа команд внедрения в no-code- и low-code-среде. Такие инструменты ускоряют запуск новых сценариев, но конфигурация нередко выходит за пределы системы: появляются Excel-файлы с настройками, ручное копирование параметров между клиентами, внешние скрипты.</p><p>Поначалу кажется, что подобная гибкость — это безусловное преимущество. Однако со временем набор настроек становится сложнее кода, между элементами возникают неочевидные связи, а разобраться в работе конкретной инсталляции все труднее. К этому добавляется нарастающая хаотичность ролей и прав доступа: временные решения превращаются в постоянные, что напрямую влияет на безопасность всей системы.</p><p><b>Архитектура как точка баланса</b></p><p>Самым важным при проектировании корпоративных платформ являются вопросы функционального дизайна и системной архитектуры. Именно они объединяют все компоненты продукта и определяют его зрелость и применимость в реальном бизнесе.</p><p>Продукты не должны сдерживать развитие платформы, а платформа — тормозить продуктовые команды.</p>]]></content:encoded>
    </item>
    <item>
      <title>Architecture as Code на практике: LikeC4 + AsciiDoc для актуальной архитектуры</title>
      <link>https://tproger.ru/articles/architecture-as-code-na-praktike-likec4-asciidoc-dlya-aktualn</link>
      <comments>https://tproger.ru/articles/architecture-as-code-na-praktike-likec4-asciidoc-dlya-aktualn?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Григорий Шелепов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/architecture-as-code-na-praktike-likec4-asciidoc-dlya-aktualn</guid>
      <description><![CDATA[<p>Architecture as Code на практике</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/architecture-as-code-na-praktike-likec4-asciidoc-dlya-aktualn">Architecture as Code на практике: LikeC4 + AsciiDoc для актуальной архитектуры</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 08 May 2026 05:45:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда-то дом могли построить, имея под рукой топор и дерево рядом с участком. Сегодня строительство устроено иначе - материалов больше, инструментов больше, а еще нужны проекты и схемы разного уровня: отдельно для конструкций, отдельно для отопления, электрики и сантехники. С ИТ-архитектурой происходит то же самое. Недостаточно только "базового набора" вроде ноутбука, монитора и клавиатуры. Нужны специализированные инструменты - для моделирования, визуализации, фиксации ограничений и совместной работы команды.</p><figure><img src="https://media.tproger.ru/user-uploads/137632/2026-04-28/dd28c0d1-6503-4e19-bf8b-e05fca245752.webp" alt="" /></figure><p>Проблема в том, что сами инструменты и подходы быстро меняются. Если архитектура остается только в Confluence и устных договоренностях, она устаревает быстрее, чем код. Через пару спринтов диаграммы уже "не про то", а архитектурные решения не проходят проверку реальностью. Именно здесь появляется <i>Architecture as Code( AaC) </i>- подход, в котором архитектура живет как инженерный артефакт: версионируется в Git, проверяется в CI/CD и развивается через MR. В этой статье разберем практическую связку <i>LikeC4 </i>для модели и <i>AsciiDoc </i>для документации.</p><p><br /></p><p>Если кратко, то</p><ul><li>Архитектура как код - это не про красивые схемы, а про проверяемые ограничения.</li><li>LikeC4 дает C4-модель как исходники (.c4) с диффами, ревью и тестами.</li><li>AsciiDoc фиксирует контекст решений, требования и обоснования рядом с моделью.</li><li>Архитектурные изменения ведутся в отдельном репозитории с собственными MR и проверками.</li></ul><p>Почему "архитектура в документах" ломается? Обычный сценарий выглядит примерно так:</p><ul><li>диаграммы создаются, но как правило не обновляются;</li><li>сервисы и контракты меняются каждую неделю;</li><li>ревью кода проходит без сверки с архитектурными ограничениями.</li></ul><p>Итог - "документированная архитектура" живет отдельно от системы.</p><p>AaC подход закрывает этот разрыв:</p><ul><li>артефакты архитектуры хранятся в отдельном архитектурном репозитории;</li><li>ограничения формулируются как правила;</li><li>правила проверяются автоматически в pipeline.</li></ul><p>Что именно кодируем?</p><p>Практически полезный минимум:</p><ul><li>контекст системы и внешние акторы;</li><li>контейнеры и ключевые компоненты;</li><li>интерфейсы, протоколы, синхронные и асинхронные взаимодействия;</li><li>инфраструктурные ограничения (например, допустимые зависимости и boundary rules);</li><li>ссылки на ADR, OpenAPI, схемы БД и эксплуатационные метрики.</li></ul><p>Здесь важно не "оцифровать все", а начать с того, что чаще всего вызывает расхождения между планом и реализацией.4.</p><p>Роль LikeC4</p><p>LikeC4 - это язык описания C4-модели и набор инструментов вокруг нее.Что это дает команде:</p><ul><li>диаграммы задаются текстом, а не мышкой;</li><li>изменения видны в git diff;</li><li>модель можно тестировать и линтить;</li><li>из модели собирается статический сайт с навигацией по представлениям.</li></ul><p>Основные возможности LikeC4 (по документации)</p><p>Официальная документация: <a href="https://likec4.dev/">likec4.dev</a></p><ul><li>DSL для описания specification, model, views и deployment-слоя в одном источнике истины;</li><li>views как проекции модели - с разным уровнем детализации, scoped views и расширением (extends) без дублирования;</li><li>предикаты для управляемого включения элементов и связей в конкретное представление;</li><li>генерация артефактов "из коробки" - статический сайт, экспорт диаграмм, интеграция в документацию и UI;</li><li>API/MCP-интеграция для доступа к архитектурной модели из внешних инструментов и AI-ассистентов.</li></ul><p>AI-skills для генерации схем LikeC4В LikeC4 есть отдельный раздел по AI-инструментам: <a href="https://likec4.dev/tooling/ai-tools/">AI Tools</a>.</p><p>Практически это дает два уровня автоматизации:</p><ul><li>Agent Skills - подключают для AI-ассистента справочник DSL и снижают риск "галлюцинаций" в синтаксисе;</li><li>MCP Server - дает LLM доступ к текущей архитектурной модели, чтобы строить и уточнять схемы на основе реального контекста проекта.</li></ul><p>Базовое подключение skills в проект:</p><p>Пример на базе моего проекта</p><p>Публичный пример с визуализацией архитектуры в LikeC4: <a href="https://shelepovg.github.io/system-design-homework">system-design</a></p><p><a href="https://shelepovg.github.io/system-design-homework"></a>Исходный код проекта: <a href="https://github.com/shelepovg/system-design-homework">system-design-code</a>Кратко о проекте:</p><ul><li>это практический стенд Architecture as Code с LikeC4-моделью;</li><li>в нем можно посмотреть структуру системы и связи между элементами в формате интерактивных представлений;</li></ul><p>Пример описания контейнера и связей:</p><p>Пример представления:</p><figure><img src="https://media.tproger.ru/user-uploads/137632/2026-04-28/07567eb3-739d-4ed3-b539-58d14068028b.webp" alt="" /></figure><p>Роль AsciiDoc рядом с модельюГрафическая модель отвечает на вопрос "что с чем связано".</p><p>AsciiDoc отвечает на вопросы:</p><ul><li>почему принято такое решение;</li><li>какие допущения и ограничения учтены;</li><li>как проверять качество и соответствие;</li><li>какие есть риски и компромиссы.</li></ul><p>Плюс - из одного источника можно получить HTML/PDF для разных потребителей: разработчики, архитектурный комитет, аудит.</p><p>Как это выглядит в репозиторияхТиповая структура для коммерческой эксплуатации:</p><ul><li>likec4/ - модель, deployment и views;</li><li>doc/ или presentations/ - объясняющая документация в AsciiDoc;</li><li>CI job в архитектурном репозитории, который валидирует модель и блокирует merge при нарушениях.</li></ul><p>При разделении репозиториев критичен процесс синхронизации:</p><ul><li>общие идентификаторы изменений (issue/ticket/ADR);</li><li>правило "нет merge в production-код без обновленной архитектурной модели".</li></ul><p>CI/CD: где появляется ценностьМинимальный pipeline для старта:</p><ol><li>Сборка представлений модели (например, npm run build:likec4).</li><li>Тесты/линтер архитектурной модели (например, npm run test:likec4).</li><li>Проверка целостности ссылок на документацию и артефакты.</li><li>Публикация статического отчета как build artifact.</li></ol><p>Даже такой базовый набор снижает риск "тихого дрейфа архитектуры", когда код уже ушел в сторону, а архитектурный репозиторий еще не обновлен.</p><p>В эпоху ИИ ценность подхода дополнительно растет - формализованная модель AaC и связанная с ней документация позволяют собирать первичные варианты архитектуры и артефактов прямо из бизнес-требований. Если в AI skills зафиксировать не только синтаксис конкретного инструмента, но и внутренние стандарты системного дизайна и оформления документов, ассистент начинает выдавать результат в привычном для команды формате. Это снижает время на старт новой инициативы и уменьшает число итераций на "доведение до корпоративного шаблона". Вместо ручной компоновки с нуля команда сразу получает основу, которую остается уточнить и проверить.</p><p>Фитнес-функции на базе LikeC4</p><p>Когда архитектурная модель поддерживается как код и регулярно обновляется, на ее основе можно строить архитектурные фитнес-функции - автоматические проверки соответствия между "как задумано" и "как работает в runtime".</p><p>В таком режиме LikeC4-модель может стать мастер-системой хранения архитектурных метаданных - источником, с которым сверяются окружения, конфигурации и реальные взаимодействия компонентов.</p><p>Примеры практических фитнес-функций:</p><ul><li>сравнение задеплоенного в Kubernetes с тем, что описано в архитектурной модели;</li><li>сравнение реальных связей компонентов (по данным кластера/сервис-меша/телеметрии) с заявленными связями в модели;</li><li>проверка values.yaml Helm-чартов на соответствие архитектурным ограничениям;</li><li>проверка наличия обязательных метаданных (owner, критичность, зависимости, ссылки на ADR/OpenAPI).</li></ul><p>Что это дает на практике:</p><ul><li>раннее обнаружение архитектурного дрейфа;</li><li>более безопасные релизы за счет fail-fast проверок в CI/CD;</li><li>прозрачный аудит изменений - от модели до фактического состояния среды.</li></ul><p>Edge cases и риски внедрения</p><p>Перед запуском подхода стоит учитывать:</p><ul><li>Избыточная детализация - если моделировать каждый класс, команда быстро устанет поддерживать модель.</li><li>Формальный adoption - без включения проверок в CI все останется "декларацией намерений".</li><li>Нет владельца модели - нужна роль или процесс, который отвечает за целостность архитектурных артефактов.</li><li>Слабая связь с runtime - полезно связывать модель с метриками/логами/трейсингом, иначе теряется трассируемость.</li><li>Рассинхрон двух репозиториев - без связки MR и единых правил merge архитектура быстро отстает от реализации.</li></ul><p>С чего начать без большого рефакторинга</p><p>Рабочий план на 2-3 итерации:</p><ol><li>Выбрать один критичный поток (например, обработку пользовательского запроса).</li><li>Описать для него C4-контекст и контейнеры в LikeC4.</li><li>Зафиксировать 3-5 ключевых ограничений (зависимости, контракты, границы).</li><li>Добавить проверки в CI как non-blocking, затем сделать их blocking.</li><li>Вынести обоснование решений и риски в AsciiDoc рядом с моделью.</li></ol><p>Такой инкрементальный старт безопаснее, чем попытка сразу перевести всю архитектуру в код.</p><p>Вывод</p><figure><img src="https://media.tproger.ru/user-uploads/137632/2026-04-29/ccadead5-24bc-40f4-8a67-b6781a9ae23f.webp" alt="" /><figcaption>вывод</figcaption></figure><p>Architecture as Code - это про управляемость изменений, а не про документацию ради документации. Связка</p><p>LikeC4 + AsciiDoc дает практичный баланс:</p><ul><li>модель архитектуры - как проверяемый код;</li><li>решения и контекст - как поддерживаемый текст;</li><li>единый процесс изменений - через PR и CI/CD.</li></ul><p>Если в проекте есть боль от "устаревших схем" и "архитектуры на словах" - этот подход обычно окупается уже на первых релизах за счет снижения архитектурного рассинхрона.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что на самом деле копирует fork() — и почему ваш пул соединений ломается</title>
      <link>https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-</link>
      <comments>https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-</guid>
      <description><![CDATA[<p>fork() копирует память процесса, но разделяет файловые дескрипторы. Разбираем реальный инцидент с Django, Celery и psycopg: как один флаг сломал все воркеры.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-">Что на самом деле копирует fork() — и почему ваш пул соединений ломается</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:24:45 GMT</pubDate>
      <content:encoded><![CDATA[<p>Все воркеры <a href="https://docs.celeryq.dev/en/stable/">Celery</a> одновременно перестали получать соединения с базой. Ни один. Ни ошибок подключения, ни таймаутов запросов — просто тишина. Метрики пула показывали норму. Инфраструктура была в порядке. Но каждая задача падала через 20 секунд с одним и тем же сообщением:</p><p>Проблема оказалась не в сети, не в базе и не в нагрузке. Проблема была в одном булевом флаге, который изменил момент открытия пула соединений — и превратил fork() из безопасной операции в мину замедленного действия.</p><blockquote><b>Ключевые выводы:</b><br />— fork() копирует память процесса (copy-on-write), но разделяет файловые дескрипторы с ядром<br />— TCP-сокеты после fork() указывают на один и тот же объект ядра — два процесса пишут в один поток<br />— threading.Lock ломается: futex-ключ привязан к физическому адресу памяти, который меняется при copy-on-write<br />— Фоновые потоки пула просто не существуют в дочернем процессе — fork() копирует только вызывающий поток<br />— Правило: никогда не держите открытые соединения перед fork()</blockquote><h2>Инцидент — что случилось</h2><p>За несколько дней до инцидента в конфигурации изменился один флаг. Нужно было, чтобы обработчики сигналов <a href="https://www.djangoproject.com/">Django</a> регистрировались внутри воркеров Celery. Существующая настройка пропускала регистрацию, если флаг не был установлен в true.</p><p>Флаг установили. Сигналы заработали. Фича уехала в прод.</p><p>QA был быстрым. Изменение выглядело изолированным: переключить флаг, убедиться, что сигналы регистрируются в Celery, готово. Никто не искал побочных эффектов, спрятанных в AppConfig.ready().</p><p>А потом тихо начало выполняться кое-что ещё. Несколько методов AppConfig.ready(), которые теперь стали активны, делали ORM-запросы при старте: создавали расписания периодических задач, проверяли записи crontab. Рутинные вещи. Ничего опасного на вид.</p><p>Но эти запросы открыли пул соединений с базой данных. В мастер-процессе. <b>До fork()</b>.</p><p>Модель конкурентности Celery по умолчанию — prefork. Не потоки, не async-воркеры. Celery использует fork() — системный вызов POSIX — для создания пула рабочих процессов. Эту деталь легко забыть, когда смотришь на код приложения. Но она становится критически важной, когда открываешь соединения с базой при старте.</p><p>В этом и была проблема.</p><h2>Что копирует fork()</h2><p>Большинство разработчиков знают поверхностный ответ: fork() создаёт дочерний процесс, который является копией родительского. Но слово «копия» скрывает важное различие, которое ядро проводит между двумя типами ресурсов.</p><h3>Память — copy-on-write</h3><p>Когда fork() выполняется, ядро не дублирует RAM немедленно. Вместо этого оно помечает таблицы страниц обоих процессов как read-only, указывая на одни и те же физические страницы. Фактическое копирование происходит только когда один из процессов пишет в страницу: ядро перехватывает page fault, выделяет новую физическую страницу, копирует содержимое и обновляет таблицу страниц. До этого момента оба процесса разделяют физическую память, не зная об этом.</p><p>Это эффективно. И именно поэтому Python-объекты, включая внутреннее состояние пула соединений, выглядят целыми в дочернем процессе. Дочерний процесс получает свою копию pool._pool, pool._lock, pool._sched. Байты на месте. Структура на месте.</p><p>Но некоторые из этих байтов указывают на ресурсы ядра. А ресурсы ядра не копируются.</p><h3>Файловые дескрипторы — общие</h3><p>TCP-сокет — это не Python-объект. Это объект ядра: struct file со счётчиком ссылок, за которым стоит struct sock с буферами отправки и получения, состоянием TCP, sequence numbers. Когда fork() выполняется, ядро вызывает dup_fd() для таблицы файловых дескрипторов родителя: каждый открытый fd дублируется в дочерний процесс, а счётчик ссылок на struct file увеличивается.</p><p>Оба процесса теперь держат fd=12. Оба указывают на один и тот же объект сокета в ядре. Один TCP-поток, два читателя и два писателя — без координации между ними.</p><p>Проводной протокол <a href="https://www.postgresql.org/">PostgreSQL</a> — stateful. Он ожидает последовательные пары запрос-ответ в одном потоке. Два процесса, чередующие байты в одном соединении, не создают два независимых разговора. Они создают мусор.</p><h3>Блокировки — сломанный futex</h3><p>threading.Lock построен на pthread_mutex_t, который внутри опирается на futex — механизм ядра Linux, использующий <i>физический адрес памяти</i> целого числа как ключ для очереди ожидания. После fork() copy-on-write может переместить страницу дочернего процесса на новый физический адрес при записи. Futex-ключ дочернего процесса расходится с родительским. futex_wake из одного процесса не будит никого в другом.</p><p>POSIX явно говорит об этом: поведение мьютекса после fork() не определено, если он не был создан с атрибутом process-shared. Python-овский Lock этот атрибут не использует.</p><h3>Фоновые потоки — просто не существуют</h3><p>fork() дублирует только вызывающий поток. Внутренний планировщик пула — отвечающий за поддержание минимального числа соединений, проверки здоровья, уведомление ожидающих — копируется как Python-объект, но не имеет соответствующего потока ОС. Его TID не существует. Любой путь кода, который зависит от его ожидания или сигнализации, заблокируется навсегда.</p><p>Дочерние процессы унаследовали пул, который выглядел целым, но был мёртв. Они вызывали pool.getconn(), пытались захватить сломанный лок, ждали 20 секунд notify, который никогда не придёт, и получали таймаут.</p><h2>Почему пул соединений ломается после fork()</h2><p>Пул соединений <a href="https://www.psycopg.org/psycopg3/docs/">psycopg</a> (ConnectionPool) хранит три вида ресурсов. Каждый ломается по-своему после fork().</p><p><b>TCP-сокеты</b> разделяются на уровне ядра. Любая попытка использовать их из двух процессов одновременно разрушает поток протокола. На практике дочерние процессы до этого даже не доходили.</p><p><b>threading.Lock</b> опирается на futex, привязанный к физическому адресу. После copy-on-write ключ дрейфует. futex_wake будит очередь по старому адресу — никого в дочернем процессе там нет.</p><p><b>Фоновые потоки</b> не существуют в дочернем процессе. Планировщик пула — Python-объект без ОС-потока. Код, зависящий от его notify, блокируется навечно.</p><p>Вот почему раньше всё работало:</p><p>До изменения флага AppConfig.ready() пропускал регистрацию периодических задач. ORM не вызывался при старте. Нет запросов — нет пула. Нет пула — нечего наследовать. Каждый дочерний процесс начинал с чистого состояния и лениво создавал собственный пул при первом обращении к базе: собственные сокеты, собственный лок, собственный фоновый поток.</p><h2>Решение — чистый fork без наследства</h2><p>Два хука сигналов Celery.</p><p>worker_before_create_process срабатывает в родительском процессе после ready(), перед каждым fork(). Он закрывает все соединения с базой по всем алиасам и уничтожает пулы соединений. К моменту выполнения fork() нет открытых TCP-сокетов, нет локов, нет фоновых потоков для наследования.</p><p>worker_process_init срабатывает в каждом дочернем процессе после fork() как второй уровень защиты — defense-in-depth, на случай если что-то было пропущено.</p><p>После обоих хуков каждый дочерний процесс пуст. Первый ORM-запрос создаёт свежий пул, который целиком принадлежит этому процессу.</p><p>Принцип простой: никогда не держите открытые соединения перед fork() — или явно закройте и уничтожьте их до форка. Это правило применимо к любому пулу соединений на основе TCP: <a href="https://www.psycopg.org/psycopg3/docs/">psycopg</a>, <a href="https://www.sqlalchemy.org/">SQLAlchemy</a>, redis-py. Технология не важна. Важно поведение ядра.</p><h2>Диаграммы — до и после</h2><h3>До изменения флага: чистый fork, каждый воркер создаёт свой пул</h3><h3>После изменения флага: пул открыт в мастере, fork() распространяет повреждение</h3><h3>После исправления: пул уничтожен перед fork(), каждый воркер стартует чисто</h3><h2>Выводы</h2><p>Настоящий урок — не в конкретном фиксе. Он в понимании того, почему пул сломался. Что fork() на самом деле копирует. Что он разделяет. Почему лок, который выглядит целым, может заблокировать дочерний процесс. Почему поток, существующий как Python-объект, может полностью отсутствовать в ОС.</p><p>Правила, которые стоит запомнить:</p><ul><li>Никогда не открывайте пул соединений до fork() — или явно уничтожьте его перед форком</li><li>fork() копирует память (лениво), но разделяет файловые дескрипторы на уровне ядра</li><li>threading.Lock после fork() — undefined behavior: futex привязан к физическому адресу, который дрейфует при copy-on-write</li><li>Фоновые потоки не дублируются — fork() копирует только вызывающий поток</li><li>Это правило касается любого пула TCP-соединений: psycopg, SQLAlchemy, redis-py — поведение ядра одинаково</li></ul><p>Невидимая часть — это взаимодействие между последовательностью запуска Django и моделью процессов Celery. Две системы, каждая из которых хорошо понятна по отдельности, делают неожиданное на стыке.</p><p><i>Адаптированный перевод статьи <a href="https://tech.daniellbastos.com.br/posts/what-fork-actually-copies/">What fork() Actually Copies</a> Даниэля Бастоса (Daniel Bastos).</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Почему ИИ-сгенерированный код создаёт долг понимания</title>
      <link>https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya</link>
      <comments>https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya</guid>
      <description><![CDATA[<p>Адди Османи объясняет, почему ИИ-инструменты создают разрыв между объёмом кода и его пониманием. Как тесты и спецификации не решают проблему и что делать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya">Почему ИИ-сгенерированный код создаёт долг понимания</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 14:00:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>Команда работает быстрее, чем когда-либо. Метрики скорости бьют рекорды. Код-ревью проходят гладко, тесты зелёные, PR-ы сливаются один за другим. Но за этим блеском скрывается расход, который ни одна метрика не фиксирует: разрыв между тем, сколько кода существует в системе, и тем, сколько из него хоть кто-то по-настоящему понимает. <a href="https://addyosmani.com/blog/comprehension-debt/">Адди Османи</a> — инженерный лидер <a href="https://www.google.com/chrome/">Google Chrome</a>, автор книги <a href="https://www.patterns.dev/posts/classic-design-patterns">Learning JavaScript Design Patterns</a> — называет этот расход <b>долгом понимания</b> (comprehension debt). И предупреждает: проценты по нему растут быстрее, чем по техническому долгу.</p><p><b>Долг понимания</b> — это растущий разрыв между объёмом кода в системе и долей этого кода, которую живые люди действительно понимают. В отличие от технического долга, который заявляет о себе трением — медленными сборками, запутанными зависимостями, ощущением ужаса при касании «того самого» модуля — долг понимания порождает ложную уверенность. Кодовая база выглядит чистой. Тесты зелёные. Расплата приходит тихо, обычно в самый неподходящий момент.</p><p><a href="https://margaretstorey.com/blog/2026/02/09/cognitive-debt/">Маргарет-Энн Стори</a> описывает студенческую команду, которая наткнулась на стену на седьмой неделе: они больше не могли вносить простые изменения, не ломая что-нибудь неожиданное. Проблемой был не грязный код. Проблемой было то, что никто в команде не мог объяснить, почему были приняты те или иные проектные решения и как разные части системы должны работать вместе. Теория системы испарилась.</p><p>Недавнее исследование <a href="https://www.anthropic.com/research/AI-assistance-coding-skills">Anthropic</a> подтверждает это количественно. В рандомизированном контролируемом эксперименте с 52 инженерами участники, использовавшие ИИ-ассистента, завершали задачу за то же время, что и контрольная группа, — но на последующем тесте на понимание кода набрали на 17% меньше баллов (50% против 67%). Наибольшее падение — в навыках отладки. Пассивное делегирование («просто сделай это») разрушает формирование навыков значительно сильнее, чем активное использование ИИ для вопросов и изучения компромиссов.</p><blockquote><b>Ключевые выводы</b><br /><br />- Долг понимания — это не технический долг. Технический долг виден; долг понимания порождает ложную уверенность.<br />- ИИ генерирует код быстрее, чем человек способен его проверить. Прежний фильтр качества стал узким местом пропускной способности.<br />- Тесты необходимы, но недостаточны: нельзя написать тест на поведение, о котором никто не подумал.<br />- Спецификации тоже не спасают: между спецификацией и работающим кодом лежат сотни неявных решений.<br />- Ни одна текущая метрика не отражает этот долг. Velocity, DORA, покрытие кода — всё зелёное, пока понимание тихо выгорает.<br />- Удешевление генерации кода не удешевляет понимание. Работа по пониманию — это и есть работа инженера.</blockquote><h2>Асимметрия скорости — ИИ генерирует быстрее, чем человек проверяет</h2><p>ИИ генерирует код значительно быстрее, чем люди способны его оценить. Звучит очевидно, но последствия легко недооценить.</p><p>Когда разработчик в команде пишет код, код-ревью всегда было узким местом — но <b>продуктивным и обучающим</b>. Чтение пулл-реквеста вынуждает вникать. Оно вскрывает скрытые предположения, ловит проектные решения, конфликтующие с архитектурой полугодовой давности, и распределяет знание о том, что кодовая база реально делает, между людьми, ответственными за её поддержку.</p><p>ИИ-сгенерированный код ломает этот цикл обратной связи. Объём слишком велик. Результат синтаксически чистый, хорошо отформатированный, внешне корректный — именно те сигналы, которые исторически вызывали уверенность при мёрже. Но поверхностная корректность — не системная корректность. Кодовая база выглядит здоровой, а понимание тихо выгорает под ней.</p><p>И инверсия резче, чем кажется. Когда код было дорого производить, сеньоры могли ревьюить быстрее, чем джуниоры писали. <b>ИИ переворачивает это: джуниор теперь генерирует код быстрее, чем сеньор способен его критически проверить.</b> Ограничивающий фактор, который делал ревью осмысленным, исчез. То, что было фильтром качества, стало проблемой пропускной способности.</p><h2>Тесты — необходимы, но недостаточны</h2><p>Инстинктивная реакция — налечь на детерминированную верификацию: юнит-тесты, интеграционные тесты, статический анализ, линтеры, форматтеры. Автоматизировать выход из узкого места ревью. Пусть машины проверяют машины.</p><p>Это помогает. Но у этого подхода есть жёсткий потолок.</p><p>Набор тестов, способный покрыть всё наблюдаемое поведение, во многих случаях окажется сложнее, чем код, который он валидирует. А сложность, о которой невозможно рассуждать, не обеспечивает безопасность. И за этим стоит более фундаментальная проблема: <b>нельзя написать тест на поведение, о котором вы не подумали</b>.</p><p>Никто не пишет тест, утверждающий, что перетаскиваемые элементы не должны становиться полностью прозрачными. Конечно, не пишет — такая возможность никому не приходила в голову. Это именно тот класс сбоев, который проскальзывает — не потому, что тесты написаны плохо, а потому, что никто не подумал туда посмотреть.</p><p>Есть и конкретный сценарий провала, который стоит назвать. <b>Когда ИИ меняет поведение реализации и обновляет сотни тест-кейсов под новое поведение</b>, вопрос сдвигается с «правильный ли этот код?» на «были ли все эти изменения тестов необходимыми, и достаточно ли у меня покрытия, чтобы поймать то, о чём я не думаю?» Тесты не могут ответить на этот вопрос. Только понимание может.</p><p>Данные это подтверждают. Разработчики, делегирующие ИИ генерацию кода, набирают менее 40% на тестах понимания. Разработчики, использующие ИИ для концептуальных вопросов — исследования компромиссов, выяснения «почему» — набирают выше 65%. Инструмент не разрушает понимание. Его разрушает то, <i>как</i> вы этот инструмент используете.</p><h2>Спецификации — тоже не спасут</h2><p>Популярное предложение: напишите подробную спецификацию на естественном языке. Включите её в PR. Ревьюьте спецификацию, а не код. Доверьтесь тому, что ИИ точно перевёл намерение в реализацию.</p><p>Это привлекательно ровно так же, как когда-то был привлекателен Waterfall. Строго определите проблему, затем исполните. Чистое разделение ответственности.</p><p>Проблема в том, что перевод спецификации в работающий код включает огромное число неявных решений — крайние случаи, структуры данных, обработка ошибок, компромиссы производительности, паттерны взаимодействия — которые ни одна спецификация никогда полностью не охватывает. <b>Два инженера, реализующие одну и ту же спецификацию, создадут системы со множеством наблюдаемых поведенческих различий.</b> Ни одна реализация не будет неправильной. Они просто будут разными. И многие из этих различий в итоге будут важны для пользователей способами, которых никто не предвидел.</p><p>Есть ещё одно наблюдение: спецификация, достаточно детальная, чтобы полностью описать программу, — это, по сути, и есть программа, только написанная на неисполняемом языке. Организационные затраты на написание спецификаций, достаточно подробных для замены ревью, вполне могут превысить выигрыш в продуктивности от использования ИИ. И вы всё ещё не проревьюили то, что было реально произведено.</p><p>Глубинная проблема в том, что «правильной» спецификации часто не существует. Требования возникают в процессе создания. Крайние случаи обнаруживаются при использовании. Предположение о том, что нетривиальную систему можно полностью описать до её создания, проверялось многократно и было признано несостоятельным. ИИ этого не меняет — он лишь добавляет новый слой неявных решений, принятых без человеческого обдумывания.</p><h2>Учитесь у истории</h2><p>Десятилетия управления качеством ПО в распределённых командах с разным контекстом и пропускной способностью коммуникации выработали реальные, проверенные практики. Они не испаряются оттого, что участник команды теперь — модель.</p><p><b>Что меняется с ИИ:</b> стоимость (резко ниже), скорость (резко выше), управленческие накладные расходы на межличностное взаимодействие (практически ноль). <b>Что не меняется:</b> потребность в человеке с глубоким контекстом системы, который поддерживает связное понимание того, что кодовая база действительно делает и почему.</p><p>Это некомфортное перераспределение, к которому вынуждает долг понимания.</p><p>По мере роста ИИ-объёмов инженер, который по-настоящему понимает систему, становится <b>более ценным, а не менее</b>. Способность посмотреть на дифф и мгновенно понять, какие поведения несут нагрузку. Помнить, почему то архитектурное решение было принято под давлением восемь месяцев назад. Отличить безопасный рефакторинг от того, который тихо сдвигает нечто, от чего зависят пользователи. Этот навык становится дефицитным ресурсом, от которого зависит вся система.</p><h2>Проблема измерения — метрики не видят этот долг</h2><p><b>Долг понимания так опасен именно потому, что ничто в текущей системе измерений его не отражает.</b></p><p>Метрики скорости (velocity) выглядят безупречно. DORA-метрики держатся стабильно. Количество PR растёт. Покрытие кода тестами — зелёное.</p><p>Комиссии по оценке производительности видят рост velocity. Они не могут видеть дефицит понимания, потому что ни один артефакт измерения результатов не захватывает это измерение. Система стимулов оптимизирует правильно то, что она измеряет. Но то, что она измеряет, больше не отражает то, что имеет значение.</p><p>Именно поэтому долг понимания коварнее технического долга. Технический долг — обычно осознанный компромисс: вы выбрали короткий путь, примерно знаете, где он лежит, можете запланировать расплату. Долг понимания накапливается невидимо, часто без осознанного решения кого-либо допустить его. Это совокупный результат сотен ревью, где код выглядел нормально, тесты проходили, а в очереди ждал следующий PR.</p><p>Организационное допущение «отревьюенный код = понятый код» больше не работает. Инженеры утверждали код, который они не до конца понимали, — и этот код теперь несёт неявное одобрение. Ответственность распределилась, а заметить это не успел никто.</p><h2>Регуляторы придут быстрее, чем вы думаете</h2><p>Каждая индустрия, которая двигалась слишком быстро, в итоге привлекала регулирование. Технологический сектор был необычно защищён от этой динамики — отчасти потому, что сбои в ПО часто обратимы, отчасти потому, что индустрия двигалась быстрее, чем регуляторы успевали реагировать.</p><p>Это окно закрывается. Когда ИИ-сгенерированный код работает в системах здравоохранения, финансовой инфраструктуре и государственных сервисах, формулировка «ИИ написал это, а мы не полностью проверили» не выдержит критики в пост-инцидентном отчёте, когда на кону жизни или значительные активы.</p><p>Команды, которые выстраивают дисциплину понимания сейчас — рассматривая настоящее понимание, а не просто прохождение тестов, как обязательное условие — окажутся в лучшем положении, когда этот момент наступит, чем команды, оптимизировавшие исключительно скорость мёржа.</p><h2>Что на самом деле требует долг понимания</h2><p>Правильный вопрос сейчас — не «как генерировать больше кода?», а «как понимать больше из того, что мы отгружаем?» — чтобы пользователи получали стабильно качественный продукт.</p><p>Этот сдвиг фокуса имеет практические следствия:</p><ul><li>Будьте безжалостно явными в том, что изменение <i>должно делать</i>, прежде чем оно будет написано.</li><li>Рассматривайте верификацию не как послесловие, а как структурное ограничение.</li><li>Поддерживайте ментальную модель системного уровня, которая позволяет ловить ошибки ИИ на уровне архитектуры, а не построчно.</li><li>Будьте честны относительно разницы между «тесты прошли» и «я понимаю, что это делает и почему».</li></ul><p><b>Удешевление генерации кода не удешевляет понимание. Работа по пониманию — это и есть работа инженера.</b></p><p>ИИ берёт на себя перевод. Но кто-то по-прежнему должен понимать, что было произведено, почему именно так и были ли эти неявные решения правильными — иначе вы просто откладываете счёт, который в конце концов придёт с полной суммой.</p><p>Вы заплатите за понимание рано или поздно. Проценты по этому долгу растут быстро.</p><h2>Частые вопросы</h2><h3>Чем долг понимания отличается от технического долга?</h3><p>Технический долг — обычно осознанный компромисс. Вы знаете, где лежит короткий путь, и можете запланировать рефакторинг. Долг понимания накапливается невидимо: код выглядит чистым, тесты зелёные, но никто в команде не может объяснить, почему система работает именно так. Расплата приходит внезапно — когда простое изменение ломает всё каскадом.</p><h3>Значит ли это, что ИИ-инструменты для кодинга вредны?</h3><p>Нет. Исследование Anthropic показало, что инструмент не разрушает понимание — его разрушает способ использования. Пассивное делегирование («сделай за меня») снижает понимание, а активное использование для концептуальных вопросов и исследования компромиссов — нет. Ключ в том, чтобы использовать ИИ как партнёра для мышления, а не как замену мышлению.</p><h3>Как измерить долг понимания в команде?</h3><p>Прямых метрик пока нет — в этом и проблема. Косвенные индикаторы: частота каскадных сбоев от «простых» изменений, время на onboarding новых разработчиков, способность членов команды объяснить архитектурные решения без обращения к коду. Если никто не может ответить «почему это сделано именно так» — долг уже высок.</p><h3>Что делать прямо сейчас?</h3><p>Три вещи. Во-первых, требуйте от каждого изменения явного описания того, <i>что</i> оно должно делать, до написания кода. Во-вторых, поддерживайте ментальную модель архитектуры — это позволяет ловить ошибки на системном уровне, а не построчно. В-третьих, различайте «тесты прошли» и «я понимаю, что это делает». Первое — автоматизация. Второе — инженерная работа.</p><h2>Выводы</h2><p>Долг понимания — это не повод отказываться от ИИ-инструментов. Это повод осознанно управлять ценой, которую мы платим за скорость. Адди Османи формулирует это точно: код стал дешёвым в производстве, но понимание дешёвым не стало. И чем больше мы генерируем, тем дороже обходится разрыв.</p><p>Оригинал статьи: <a href="https://addyosmani.com/blog/comprehension-debt/">Comprehension Debt — the hidden cost of AI generated code</a> (Addy Osmani, март 2026).</p>]]></content:encoded>
    </item>
    <item>
      <title>Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</title>
      <link>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</link>
      <comments>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</guid>
      <description><![CDATA[<p>30 лучших уроков из 113 по масштабированию Django-монолита до 1 000 000 строк: архитектура, тестирование, мониторинг, безопасность, деплой и карьерный рост.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto">Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Что можно узнать из монолита на миллион строк кода? Джек Кинселла проработал 7 лет техлидом и CTO в <a href="https://www.morphmarket.com/">MorphMarket</a> — маркетплейсе на стеке <a href="https://www.djangoproject.com/">Django</a> / <a href="https://react.dev/">React</a> (TS) / React Native с командой из 20 человек. За это время кодовая база выросла до 1 000 000 строк, а Джек прошёл путь от первого инженера до технического директора.</p><p>Он опубликовал 113 уроков, которые вынес из этого опыта. Мы отобрали самые ценные, сгруппировали по темам и адаптировали для русскоязычной аудитории. Каждый урок — практический: его можно взять и применить в своём проекте уже сегодня.</p><blockquote><b>Ключевые выводы</b><br /><br />— Кэширование — последняя мера. Сначала исправьте модель данных, индексы и N+1-запросы.<br />— E2E-тесты на happy path дают в 10 раз больше отдачи, чем юнит-тесты на отдельные методы.<br />— Доведите Sentry до inbox zero — это единственный самый мощный шаг для качества продукта.<br />— Не обвиняйте конкретного разработчика за баг в проде: это всегда каскад из 3+ ошибок системы.<br />— Деплой за 2 минуты вместо 28 — достижимо, и это меняет культуру принятия решений.</blockquote><h2>Про архитектуру и масштабирование</h2><h3>Используйте сервисные объекты вместо раздувания моделей</h3><p>В фреймворках, где модели строятся вокруг существительных (Product, User, Auction), логика неизбежно скапливается в этих классах. Со временем каждый из них превращается в монстра на тысячи строк. Решение — выносить процессы в отдельные сервисные объекты: PlaceAuctionBidService, ProductBillingService. Суть — перестать мыслить существительными и начать мыслить глаголами.</p><h3>Одна и та же вещь, реализованная дважды — провал технического лидерства</h3><p>Если в кодовой базе есть два разных клиента к одному платёжному провайдеру, три файла с функциями для работы с датами или дублирующиеся UI-компоненты — это симптом отсутствия ответственного за архитектуру. Создавайте очевидные, легко находимые места для общего кода (common/datetime.py, common/geo.py) и отмечайте переиспользуемый код на ревью.</p><h3>Полиморфные связи в БД — почти всегда ошибка</h3><p>ORM-код для полиморфных отношений становится крайне запутанным, и вы теряете защиту через foreign key constraints. Проще добавить nullable FK для каждой связанной модели и написать абстракции на уровне запросов.</p><h3>Дефолтные фильтры и сортировки в ORM — всегда ошибка</h3><p>Скрытые дефолты вроде order_by('-id') или filter(is_deleted=False) становятся миной замедленного действия, когда в команде 20+ человек. Новый разработчик не знает про неявное поведение — и получает баг. А скрытый ORDER BY портит аналитические запросы, потому что ORM добавляет ключ сортировки в SELECT.</p><h3>Вся инфраструктура — в коде</h3><p>Когда Джек пришёл в MorphMarket, все 27 крон-задач были настроены вручную на продакшн-сервере. Большинство отсутствовали на staging. Перенос в код (а затем конфигурация AWS/Heroku/CloudFlare/Sentry через <a href="https://www.terraform.io/">Terraform</a>) сделал все окружения похожими друг на друга — и, что важно, сделал инфраструктуру доступной для чтения AI-агентам.</p><h2>Про тестирование</h2><h3>E2E &gt; интеграционные &gt; юнит-тесты</h3><p>Пользователям важно, чтобы система работала целиком. Один E2E-тест на happy path каждой ключевой фичи (<i>регистрация, покупка, создание листинга</i>) даёт на порядок больше уверенности, чем десятки юнит-тестов. Детали дорабатывайте интеграционными и юнит-тестами — они быстрее и проще в поддержке.</p><h3>Автоматические тесты для шаблонных фич</h3><p>В MorphMarket написали систему, которая автоматически тестирует около 300 админ-страниц: парсит имя страницы, находит фабрику, создаёт запись в БД и проверяет, что страница открывается. Тот же подход можно применить к любому CRUD-эндпоинту без выделенного теста.</p><h3>Изолируйте тесты полностью</h3><p>Хрупкость тестов убивает мотивацию команды. Основные ловушки:</p><ul><li>Всё, что связано со временем — замораживайте или мокайте</li><li>Фиксируйте seed для всех источников случайности — в каждой библиотеке</li><li>Запретите реальные HTTP-запросы в не-E2E тестах (используйте VCR-подобные библиотеки)</li><li>Отдельный Redis для тестов с автоочисткой перед каждым тестом</li><li>Тестовая среда не должна читать ни одной переменной из вашего .env</li></ul><h3>Мокайте только внешние объекты</h3><p>Мок — полезный инструмент, но только для внешних зависимостей. Мокать внутренние методы тестируемого класса — антипаттерн. Если можно переписать тест без мока без потери скорости — так и сделайте.</p><h3>CI должен проверять не только ошибки, но и раздражающие мелочи</h3><p>В MorphMarket CI также ловит новые warning-и в тестах, рассинхронизацию SDK с бэкендом и забытые миграции в Django. Чем раньше замечена мелочь, тем дешевле её исправить.</p><h2>Про отладку и мониторинг</h2><h3>Sentry до inbox zero — самый мощный шаг к качеству</h3><p>Когда Джек пришёл в компанию, Sentry показывал 1 000 ошибок в час. После агрессивной фильтрации (игнор старых браузеров, расширений, пометка JavaScript-ошибок уникальным идентификатором) и починки реальных багов — меньше 1 ошибки в час. Результат: каждая новая ошибка стала <i>событием</i>, на которое команда реагирует немедленно.</p><h3>Dead Man Switch — мониторьте то, что должно происходить</h3><p>Исключения шумят и легко ловятся. А вот отсутствие события — нет. Если крон-система перестала работать или бэкапы БД не снимаются — вы узнаете об этом только через heartbeat-мониторы. Настройте их.</p><h3>Username в каждой строке лога</h3><p>По умолчанию в логах — только IP-адрес. Но IP ротируется, создаёт лишнее перенаправление при дебаге и не показывает картину при использовании нескольких устройств. Поместите текущий запрос в thread-local и добавьте username в каждую запись.</p><h3>Трипвайр-алерты на 20 самых важных эндпоинтов</h3><p>Деградация производительности подкрадывается незаметно — инкрементальные фичи постепенно ломают индексы и кэши. Установите порог в несколько стандартных отклонений от нормы на ключевых эндпоинтах. Команда узнает о проблеме в течение часов после деплоя, а не через неделю жалоб.</p><h3>Громко падайте при ошибках</h3><p>Не глушите исключения except: pass. Паттерн от Джека: <i>крашиться локально, мягко отказывать в проде — но всегда отправлять ошибку в Sentry/логи</i>. Чем раньше заметили — тем меньше ущерб.</p><h2>Про работу в команде</h2><h3>Не обвиняйте одного человека за баг в проде</h3><p>Любой инцидент — это каскад минимум трёх провалов: автор не заметил, ревьюер пропустил, QA не покрыло, тесты не проверили, архитектура допустила класс ошибки. Обвинение одного человека мешает команде видеть системные причины.</p><h3>Все разработчики должны быть немного фулстек</h3><p>В MorphMarket убрали строгое деление на фронтенд/бэкенд/ops. Переобучение заняло год, но окупилось: фронтендер добавляет поле в БД сам, а не ждёт бэкендера — это сокращает время доставки фич.</p><h3>Пятницы технического долга</h3><p>Разумный компромисс между CEO, который хочет фичи, и инженерами, которым нужна чистота кода: с понедельника по четверг приоритеты задаёт продукт, а по пятницам — CTO. Бонус: инженеры отдыхают на глубоких задачах перед выходными.</p><h3>Запретите длинные PR</h3><p>В MorphMarket был период, когда PR висели по 3 месяца и накапливали тысячи изменений. Их невозможно ревьюить, они конфликтуют с master и создают огромный деплой-риск. Новое правило: мерж в master каждые 1–2 дня, максимум — раз в неделю. Побочный эффект: старшие разработчики дают фидбэк раньше, и драматические переписывания случаются реже.</p><h3>Поощряйте общение в удалённых командах</h3><p>Джек начал с ежедневных постов о прогрессе, потом стал публично задавать вопросы (даже на которые знал ответ), делиться изменениями в коде и просить мнения. Это постепенно снизило барьер и сделало общение нормой, а не исключением.</p><h2>Про производительность</h2><h3>N+1-запросы — враг номер один в ORM</h3><p>Проблема проявляется на крупных эндпоинтах, которые постепенно расширяют разные авторы: новые фильтры и параметры создают спорадические N+1, которые возникают только при определённом сочетании фильтров и объёме данных. Настройте APM (например, <a href="https://newrelic.com/">New Relic</a>) для автоматического обнаружения N+1 и обучите команду ловить их на ревью.</p><h3>Кэширование — последняя мера</h3><p>Код кэширования чудовищно сложен в поддержке. Сначала исправьте модель данных, допишите индексы, уберите N+1, оптимизируйте алгоритмы. И только после этого — кэш. Джек рассказывает о случаях, когда наивный кэш работал на малых объёмах, но убивал Redis (он однопоточный!) при росте данных на два порядка.</p><h3>Таймауты на всех уровнях — иммунная система сервиса</h3><p>Веб-запрос должен завершиться за 25 секунд — иначе убивается. Да, для длинных задач придётся использовать фоновые задачи, но без таймаутов одна тормозящая внешняя API может съесть все потоки сервера и уронить всё.</p><h3>Выгружайте работу в фоновые задачи</h3><p>Отправка email, push-уведомления, обработка вебхуков от платёжных систем — всё это должно уходить в очередь. Веб-процесс обязан оставаться быстрым.</p><h3>ORDER BY id вместо ORDER BY created</h3><p>Если поле created неизменяемо и совпадает по порядку с id — сортируйте по id. Индекс на id уже есть бесплатно в каждой таблице.</p><h3>Удаляйте ненужные данные</h3><p>В MorphMarket некоторые таблицы копили 10 лет данных — логи IP-адресов, записи push-уведомлений, сообщения девятилетней давности. Скрипт очистки делает эти таблицы компактнее и быстрее.</p><h2>Про безопасность и надёжность</h2><h3>Начните с модели угроз: что может получить атакующий?</h3><p>Для MorphMarket основные вектора: захват аккаунтов (фикс — обязательный 2FA), спам-фишинг (капча на регистрации + фильтры сообщений), DDoS (<a href="https://www.cloudflare.com/">Cloudflare</a> + rate limit на nginx + кэш ключевых эндпоинтов). Универсального подхода нет — каждый сервис должен понимать свою специфику.</p><h3>CHECK-ограничения в БД — лучше предотвратить, чем чинить</h3><p>Раньше в MorphMarket крон-задача раз в сутки проверяла целостность данных и слала алерты. Всё заменили на CHECK constraints на уровне БД — некоторые проверяют до 16 операций над разными колонками. Невалидные данные просто не могут быть записаны.</p><h3>Вебхуки — рассадник race conditions</h3><p>Действие в коде может вызвать вебхук, который прилетит быстрее, чем данные запишутся в БД. Получатель вебхука прочитает устаревшее состояние и может затереть свежие данные. Решение: откладывайте действия, вызывающие вебхуки, до on_commit в БД.</p><h3>Логируйте намерение перед побочным эффектом — особенно для платежей</h3><p>Перед списанием с карты или возвратом пишите в лог: <i>"bill user XYZ $100 for Y"</i>. Простой Write-Ahead Log спасал MorphMarket в нескольких инцидентах.</p><h2>Про деплой</h2><h3>Деплой должен быть быстрым — действительно быстрым</h3><p>MorphMarket сократил деплой с 28 минут до 2. Как: разделили фронтенд и бэкенд, распараллелили, перешли на Rust-based компиляцию JS, вынесли сборку на мощные машины (MacBook M-series вместо commodity Heroku), убрали из слага всё ненужное, почистили старые ассеты из S3.</p><h3>Smoke-тест после каждого деплоя в прод</h3><p>Автоматическая проверка ключевых страниц через <a href="https://playwright.dev/">Playwright</a> после каждого деплоя. Если последние 150 деплоев были скучными — соблазн перестать проверять вручную огромен. Автоматический smoke-тест не устаёт и не теряет бдительность.</p><h3>Не деплойте ничего рискованного в пятницу</h3><p>Баг может проявиться вечером или в субботу, когда никого нет. Мелкие хотфиксы и изменения в админке — можно. Всё остальное — до четверга.</p><h3>Плейбук для инцидентов при деплое</h3><p>Короткие инструкции: как убить залипшие соединения к БД, точечно почистить кэш (без flush, который разлогинит всех), форсировать обновление ассетов. Обученная команда сокращает даунтайм в разы.</p><h2>Про карьерный рост — от тимлида до CTO</h2><h3>Научитесь говорить о производительности сотрудников</h3><p>Джек признаётся, что панически боялся этих разговоров, потому что не любит конфликты. Но реальность проста: лучше открыто сказать человеку, где он не дотягивает, чем копить раздражение и уволить неожиданно. Большинство людей способны расти под менторингом — Джек это недооценивал.</p><h3>Документ ожиданий от сотрудника</h3><p>Если написать <i>"мы ценим, когда вы решаете проблемы в #bug-discussion до того, как они дойдут до CTO"</i>, люди действительно начнут так делать. Мотивированный сотрудник хочет ясности. Он не может прочитать ваши мысли.</p><h3>Одно крупное изменение за раз</h3><p>Внедряете E2E-тесты? Доведите до стабильности, прежде чем браться за линтеры. Если система сделана плохо — команда не будет ей доверять и не будет использовать. Плюс есть предел того, сколько изменений люди могут усвоить одновременно.</p><h3>CLI-инструменты + AI = максимальный рычаг</h3><p>AI отлично работает с текстом. CLI-инструменты тоже. Дайте AI доступ к продакшн-логам, отчётам об ошибках, read-only реплике БД, CI-результатам и REPL — и получите огромный множитель продуктивности. Джек называет это <i>"наблюдаемость через CLI — ваш главный усилитель для AI"</i>.</p><h2>FAQ</h2><h3>Почему монолит, а не микросервисы?</h3><p>Монолит на миллион строк с 20 разработчиками — рабочая модель, если соблюдать дисциплину. Микросервисы добавляют сетевую сложность, и для команды такого размера overhead координации перевешивает выгоды. Джек не утверждает, что микросервисы плохи — он утверждает, что переход оправдан только при конкретной боли, а не как дефолтный выбор.</p><h3>С чего начать, если в проекте уже бардак?</h3><p>С Sentry до inbox zero и таймаутов на веб-запросы. Первое даёт видимость реальных проблем, второе предотвращает каскадные отказы. Дальше — по статье: N+1, изоляция тестов, сервисные объекты.</p><h3>Насколько эти уроки применимы за пределами Django?</h3><p>Джек 17 лет работал с Rails, Laravel, Express и другими фреймворками — и подчёркивает, что большинство уроков переносимы. N+1, дефолтные сортировки, race conditions в вебхуках, инфраструктура как код — всё это не привязано к конкретному стеку.</p><h3>Правда ли, что E2E-тесты лучше юнит-тестов?</h3><p>Не <i>лучше</i>, а дают больше отдачи на первом этапе. Один E2E-тест на happy path покрывает весь стек. Юнит-тесты нужны для деталей и граничных случаев, но начинать покрытие лучше сверху вниз.</p><h2>Выводы</h2><p>113 уроков Джека Кинселлы — это не теория из книг, а выжимка из 7 лет ежедневной работы над монолитом, который обслуживает реальных пользователей. Главные принципы:</p><ul><li>Предотвращайте, а не чините — CHECK constraints, таймауты, линтеры</li><li>Делайте невидимое видимым — логи, мониторинг, Sentry до inbox zero</li><li>Инвестируйте в скорость обратной связи — быстрый деплой, быстрый CI, короткие PR</li><li>Доверяйте людям и системе, а не контролю — fullstack-навыки, плейбуки, документ ожиданий</li></ul><p>Оригинальная статья: <a href="https://www.semicolonandsons.com/articles/scaling-a-monolith-to-1m-loc-113-pragmatic-lessons-from-tech-lead-to-cto">Scaling a Monolith to 1M LOC: 113 Pragmatic Lessons from Tech Lead to CTO</a> (Semicolon &amp; Sons, Jack Kinsella).</p><p>Если знаете коллегу, который борется с растущим монолитом — отправьте ему эту статью. А в комментариях расскажите, какой урок оказался самым неожиданным для вас.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что случилось с WebAssembly — и почему вы не заметили, как он победил</title>
      <link>https://tproger.ru/translations/chto-sluchilos-s-webassembly---i-pochemu-vy-ne-zametili--kak-on-po</link>
      <comments>https://tproger.ru/translations/chto-sluchilos-s-webassembly---i-pochemu-vy-ne-zametili--kak-on-po?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-sluchilos-s-webassembly---i-pochemu-vy-ne-zametili--kak-on-po</guid>
      <description><![CDATA[<p>WebAssembly не заменил JavaScript — и не должен был. Figma, Cloudflare, Godot, Squoosh уже используют Wasm. Разбираем, как Wasm стал мостом между языками.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-sluchilos-s-webassembly---i-pochemu-vy-ne-zametili--kak-on-po">Что случилось с WebAssembly — и почему вы не заметили, как он победил</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Языки программирования]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[WebAssembly]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 27 Mar 2026 16:15:20 GMT</pubDate>
      <content:encoded><![CDATA[<p>В каждом обсуждении WebAssembly найдётся комментарий в стиле «а что с ним стало?». Его рекламировали как революцию. Мы не видим сайтов, полностью написанных на Wasm. Он провалился? Это новый JVM-апплет?</p><p>Нет. WebAssembly победил — просто не так, как все ожидали.</p><p><b>Главное:</b> WebAssembly не заменил JavaScript в браузере — и не должен был. Его главная роль — мост между языками. Figma, Godot, Squoosh, Cloudflare Workers, Zellij — все они используют Wasm, но вы этого не замечаете — и это нормально.</p><h2>Где Wasm уже работает</h2><ul><li><b>Figma</b> — конвертирует C++ кодовую базу в браузерное приложение + запускает пользовательские плагины в песочнице через QuickJS+Wasm</li><li><b>Godot</b> — сборка игр для веба</li><li><b>Squoosh.app</b> — использует C/C++ библиотеки сжатия изображений прямо в браузере</li><li><b>Stackblitz</b> — веб-контейнеры на Wasm</li><li><b>Ruffle</b> — эмулятор Flash в браузере</li><li><b>Cloudflare Workers</b> — запуск недоверенного кода через V8 isolates</li><li><b>Zellij, Envoy, Lapce</b> — экосистема плагинов на Wasm</li></ul><h2>Что такое WebAssembly на самом деле</h2><p>WebAssembly — это <b>язык</b>. Более точно — байткод, похожий на JVM bytecode, но с меньшим API, более строгими гарантиями безопасности и без мнений о том, как управлять памятью.</p><p>Он достаточно низкоуровневый, чтобы чисто компилироваться под большинство архитектур без значительных потерь скорости. При этом Wasm-программа не может ничего без явного разрешения хоста — ни читать файлы, ни ходить в сеть. Всё внешнее — через импорты.</p><h2>Цель компиляции, не язык разработки</h2><p>В Wasm компилируются десятки языков: <b>Rust, C, Zig, Go, Kotlin, Java, C#</b>. Даже интерпретируемые языки работают — их рантаймы компилируются в Wasm (<b>Python</b> через Pyodide, <b>PHP, Ruby</b>). Есть и языки, которые компилируются исключительно в Wasm: <b>AssemblyScript, Grain, MoonBit</b>.</p><p>Ваш браузер уже умеет запускать Wasm. Но есть и автономные рантаймы: <b>Wasmtime</b>, <b>WasmEdge</b>, <b>Wasmer</b> — аналоги JVM, но для Wasm.</p><h2>Безопасность — главное преимущество</h2><p>Всё внешнее взаимодействие — явные импорты от хоста. Это даёт <b>изоляцию на уровне процесса внутри одного процесса</b>. Cloudflare запускает недоверенный код через V8 isolates — старт <b>в 100 раз быстрее</b>, чем отдельный процесс. Fermyon заявляет о старте менее чем за миллисекунду.</p><h2>Мост между языками — главная роль</h2><p>Самое распространённое применение — <b>мост между языками</b>. В большинстве случаев Wasm прозрачен для вас — какая-то библиотека просто использует его в дереве зависимостей. Это обработка изображений, OCR, физические движки, рендеринг, базы данных, парсеры.</p><h2>Ограничения</h2><ul><li>В браузере Wasm работает через тот же пайплайн, что и JS — потолок на производительность</li><li>Пересечение границы хост-программы стоит дорого — пост-мортем Zaplib показал, что постепенная миграция может не дать выигрыша</li><li>Нет нативного строкового типа — системные API приходится пересоздавать, WASI помогает частично</li><li>Самые компактные бинарники даёт Zig, самые тяжёлые без оптимизации — Rust</li></ul><h2>Почему кажется, что ничего не произошло</h2><p>Wasm-инструменты массово используются <b>авторами библиотек</b>, а не разработчиками приложений. Внутренности непрозрачны — и это нормально. Многие ожидали, что можно будет обойтись без .js файлов вообще — это крайне маловероятно, ни один вендор браузеров не работает над этим.</p><p>Фреймворки <b>Blazor</b> (.NET) и <b>Leptos</b> (Rust) позволяют писать веб-приложения без прямого контакта с JS. Стандартизация идёт: <b>WasmGC</b> уже в Chrome, Firefox и Safari; <b>Component Model</b> развивается в Bytecode Alliance.</p><h2>FAQ</h2><h3>WebAssembly быстрее JavaScript?</h3><p>Некорректный вопрос — скорость зависит от рантайма. Но конструкции Wasm хорошо ложатся на современное железо, поэтому в вычислительных задачах Wasm часто быстрее.</p><h3>Wasm заменит JavaScript?</h3><p>Крайне маловероятно. JS остаётся языком браузера, Wasm дополняет его там, где нужны вычисления, безопасность или доступ к библиотекам из других языков.</p><h3>С чего начать?</h3><p>Попробуйте <a href="https://github.com/nicolo-ribaudo/watlings">watlings</a> — упражнения по ручному написанию WAT в стиле rustlings. А для практического применения — wasm-pack для Rust.</p><p>Источник: <a href="https://emnudge.dev/blog/what-happened-to-webassembly/">What Happened To WebAssembly</a> — EmNudge</p>]]></content:encoded>
    </item>
    <item>
      <title>Как энергетика собирает ядро под требования КИИ</title>
      <link>https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii-2</link>
      <comments>https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii-2?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Виталий Попов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii-2</guid>
      <description><![CDATA[<p>Как в энергетике строят корпоративное ИТ-ядро под требования КИИ: от сегментации и домена до dual-OS, fallback-сценариев и ОПЭ.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii-2">Как энергетика собирает ядро под требования КИИ</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Информационная безопасность]]></category>
      <category><![CDATA[LDAP]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 24 Mar 2026 08:35:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В энергетике ИТ-ландшафт проектируется от ограничений, а не от продуктов. Сначала – сегментация, изоляция, модель угроз. Потом – все остальное. Архитектура рождается из этих рамок. Отсюда ключевой вопрос: каким должно быть корпоративное ядро в компаниях с КИИ, где требования безопасности задают рамки всей архитектуры? Формальные инструкции здесь не работают – зато работает набор практик, который стабильно выдерживает пилоты и ОПЭ.</p><h2>Корпоративное ядро: как оно устроено</h2><p>Сразу важно оговориться: корпоративное ядро и КИИ-контуры – это разные среды с разными задачами. В технологических сегментах КИИ требования строже, а фокус смещен в сторону изоляции и максимального импортозамещения. Корпоративная среда, о которой мы и говорим, проще по функционалу и ориентирована на поддержку пользователей и управленческих процессов.</p><p>В зависимости от зрелости архитектуры и глубины легаси корпоративный сегмент может быть как жестко отделен от КИИ-контуров, так и существовать с ними в управляемой гибридной модели. В обоих случаях именно требования КИИ задают архитектурные рамки, в которых проектируется корпоративная среда</p><p>В корпоративном сегменте энергетики ядро состоит из набора базовых сервисов: каталог, почта, коммуникации, документооборот, файловый слой, рабочие места и защита. Его фундамент – доменная структура, часто на Alt Domain или RedADM (службы на базе Samba DC): эти решения без сюрпризов проходят проверки совместимости и корректно работают с Kerberos и LDAP.</p><p>В реальных проектах переход к новому домену почти всегда начинается с гибридного режима. Старый Active Directory продолжает хранить SID и привязки приложений – без trust-связей массовую миграцию просто не запустить. Так домен становится точкой опоры для всех остальных компонентов. Почта – один из первых сервисов в корпоративном сегменте, который начинает жить по доменной модели: учетные записи, группы, права. И чаще всего именно она на прикладном уровне первой замечает любые отклонения в каталоге.</p><p>Остальные коммуникации строятся по той же схеме: централизованная авторизация, согласованные журналы и стабильные узлы связи. От этой части инфраструктуры не ждут чего-то сверхестественного, только спокойной, предсказуемой работы под нагрузкой.</p><p>Документооборот и файловый сервис – отдельный слой. Для пользователя это привычные папки с доступом к документам, но под капотом здесь ролевая модель, оргструктура и строгие ACL. Когда включается централизованная модель прав, самодельные сетевые диски исчезают из контура, а файловый слой превращается в управляемую систему доступа.</p><p>На уровне ниже находится невидимый пласт сервисов, на которых держится все ядро: DNS/DHCP домена, репликация, политики и настройки безопасности. Ошибки в любом из них могут спровоцировать каскад проблем – от задержек авторизации до потери доступа у части пользователей. На такие сценарии и заточены внутренние ППИ/ПМИ каждого компонента. Сценарии отрабатывают вручную, фиксируют протоколы, закрывают замечания – и только после этого переходят к пилоту.</p><p>Все это – «серверная» часть ядра. Но самый сложный участок начинается там, где архитектура сталкивается с пользователями – на АРМ (рабочих станциях). Здесь требуется подключение к каталогу, перенос профилей, миграция данных, проверка приложений, контроль целостности и унификация конфигураций.</p><p>В корпоративном сегменте на практике применяются переходные схемы – гибридный режим домена и поэтапная миграция пользовательских сервисов. В КИИ-контуре такие компромиссы возможны далеко не всегда</p><p>Если каждый компьютер конфигурировать по-своему, стабильного поведения сотен рабочих мест не добиться. Поэтому рабочие станции собирают из стандартизированных образов – виртуальных или аппаратных.</p><p>В рамках такой стандартизации на пользовательском уровне и появляется частичный dual-OS: критичные приложения остаются в Windows, а пользовательская среда переезжает в Linux. Это самый безопасный способ пройти миграцию без остановки процессов.</p><p>Во всех компонентах ядра действует единое правило: никакой архитектурной импровизации. Инфраструктура должна быть предсказуемой. Иначе она не выдержит эксплуатацию в среде, где архитектура определяется требованиями КИИ</p><h2>Как выглядит внедрение на практике</h2><p>На проектах внедрение почти всегда проходит в три этапа.</p><ol><li>Прототипирование – создание в миниатюре будущей инфраструктуры и выявление несовместимостей.</li><li>Настройка паттернов – сборка образов, настройка групп, OU, профилей, fallback-логики.</li><li>Переходный период – введение dual-OS, выравнивание политик и подготовка к ОПЭ.</li></ol><p>Прототипирование занимает от трех до шести месяцев. На этом этапе всплывает все, что невозможно увидеть на этапе проектирования. Где-то рабочие станции не загружаются на новых образах без обновления BIOS. Где-то плоттеры, сканеры и терминалы требуют ручной настройки. Профили Windows при переносе в Linux теряют ярлыки, MIME-ассоциации и политики. Если корпоративный домен в гибридном режиме, любое расхождение по времени, сбой Kerberos или конфликт GPO могут вывести из строя авторизацию на половине контура.</p><p>После пилота начинается настройка паттернов – определяем, какие драйверы исключать из образа, как собирать профили, какие группы синхронизировать, какие параметры ядра уменьшают количество сбоев, как организовать корректный fallback.</p><p>Но часть задач паттернами не решить – слишком много рабочих мест держится на Windows-приложениях. Поэтому dual-OS закрепляется как рабочая норма в корпоративном контуре: пользовательский профиль — в Linux, технологические приложения — в Windows, а архитектура постепенно приводится к единому набору политик, профилей и прав.</p><p>Откат в этом контуре обязателен. Пользователь должен иметь возможность вернуться в привычную среду в любой момент. На практике команда стремится уложиться в 10–15 минут – иначе миграционный участок становится неуправляемым</p><p>Даже после перехода в ОПЭ двухслойная конфигурация сохраняется. Часть сервисов – на новой платформе, часть – на старой. Пока среды существуют параллельно, они должны быть синхронны. Расхождение в правах, политиках или репликации моментально ломает согласованность контуров – и превращается в локальный инцидент.</p><h2>Информационная безопасность как каркас архитектуры</h2><p>Эта архитектура не может существовать без четкой рамки ИБ. Причем ИБ здесь – не надстройка, а несущая конструкция. Она определяет, что допустимо в контуре, а что нет, и где проходят реальные границы риска.</p><p>Виртуализацию разрешают только там, где можно контролировать изоляцию гостевых систем. Каналы управления – только внутри защищенных сегментов. Требования к виртуализации опираются на ГОСТ 56938: разделение зон исполнения, защита потоков управления, никаких «черных ящиков» в обновлениях.</p><p>Внешние сетевые зависимости исключаются, если они не описаны в проекте и не находятся под контролем заказчика. Неконтролируемые каналы передачи данных запрещены. Поэтому к Kerberos, репликации и маршрутам авторизации относятся как к минному полю: любая двусмысленность – это уже риск.</p><p>Сегментация подчиняется тому же принципу. Рабочие станции, серверы и хранилища данных разделены так, чтобы сбой в одном контуре не влиял на другие. Отсюда и значительные затраты времени на проверку, отладку подсетей и синхронизацию политик.</p><p>С данными та же история – они требуют отдельного контура контроля. Кто имеет доступ, где лежат журналы, что мы считаем инцидентом – эти вопросы нельзя решать по остаточному принципу. Ответы на них должны быть зашиты в архитектуру подсистем с самого начала.</p><p>И все это не имело бы смысла без гарантированного восстановления: система должна уметь возвращать себя в рабочее состояние через резервные копии, откат и проверенные сценарии. Например, компания в финсекторе после отказа всей инфраструктуры, потери ЦОД должна заработать через 2 часа.</p><h2>Стандартизация через эксплуатацию</h2><p>Когда среду удается стабилизировать на первой площадке, возникает главный вопрос: можно ли повторить тот же результат без пересборки с нуля? В энергетике – можно.</p><p>Конечно же, у компаний в разных регионах – своя история, парк оборудования или глубина легаси. Но архитектурные требования одинаковые везде. Они и формируют единый «слепок» корпоративного ядра при сохранении изоляции КИИ-контуров: правила для каталога, почты, коммуникаций и подготовки рабочих мест.</p><p>Если система стабильно прошла ОПЭ на одной площадке, она, как правило, так же ведет себя и на других</p><p>В этот момент внедрение перестает быть разовым проектом и превращается в последовательный сценарий. Архитектуру не приходится пересобирать – запускается отлаженная цепочка шагов с заранее понятными точками риска и стабильности. А полевые практики – унифицированные образы, dual-OS, fallback-механизмы – закрепляются как формальные требования.</p><h2>Импортонезависимое ядро – основа цифровой устойчивости</h2><p>В корпоративном сегменте энергетики импортонезависимое ядро давно перестало быть вопросом выбора платформ или брендов. Оно формируется как совокупность эксплуатационных ограничений, в которых заранее определены допустимые сценарии работы, отказов и восстановления.</p><p>Ключевым критерием здесь становится не функциональная насыщенность и не гибкость, а управляемость среды. Архитектура считается состоятельной тогда, когда ее поведение предсказуемо при изменениях конфигурации, обновлениях и инцидентах — и не требует ручной донастройки на каждом участке.</p><p>Поэтому внедрение импортонезависимого ядра — это не проект по замене ИТ-ландшафта, а переход к фиксированной эксплуатационной модели. В ней требования информационной безопасности заложены в архитектуру изначально, а корпоративная среда рассматривается как управляемый контур, рассчитанный на долгую и стабильную работу, а не на постоянную реконфигурацию.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как энергетика собирает корпоративное ядро под требования КИИ</title>
      <link>https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii</link>
      <comments>https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Виталий Попов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii</guid>
      <description><![CDATA[<p>Как в энергетике строят корпоративное ИТ-ядро под требования КИИ: от сегментации и домена до dual-OS, fallback-сценариев и ОПЭ.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-energetika-sobiraet-korporativnoe-yadro-pod-trebovaniya-kii">Как энергетика собирает корпоративное ядро под требования КИИ</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 22 Feb 2026 09:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В энергетике ИТ-ландшафт проектируется от ограничений, а не от продуктов. Сначала – сегментация, изоляция, модель угроз. Потом – все остальное. Архитектура рождается из этих рамок. Отсюда ключевой вопрос: каким должно быть корпоративное ядро в компаниях с КИИ, где требования безопасности задают рамки всей архитектуры? Формальные инструкции здесь не работают – зато работает набор практик, который стабильно выдерживает пилоты и ОПЭ.</p><h2>Корпоративное ядро: как оно устроено</h2><p>Сразу важно оговориться: корпоративное ядро и КИИ-контуры – это разные среды с разными задачами. В технологических сегментах КИИ требования строже, а фокус смещен в сторону изоляции и максимального импортозамещения. Корпоративная среда, о которой мы и говорим, проще по функционалу и ориентирована на поддержку пользователей и управленческих процессов.</p><p>В зависимости от зрелости архитектуры и глубины легаси корпоративный сегмент может быть как жестко отделен от КИИ-контуров, так и существовать с ними в управляемой гибридной модели. В обоих случаях именно требования КИИ задают архитектурные рамки, в которых проектируется корпоративная среда</p><p>В корпоративном сегменте энергетики ядро состоит из набора базовых сервисов: каталог, почта, коммуникации, документооборот, файловый слой, рабочие места и защита. Его фундамент – доменная структура, часто на Alt Domain или RedADM (службы на базе Samba DC): эти решения без сюрпризов проходят проверки совместимости и корректно работают с Kerberos и LDAP.</p><p>В реальных проектах переход к новому домену почти всегда начинается с гибридного режима. Старый Active Directory продолжает хранить SID и привязки приложений – без trust-связей массовую миграцию просто не запустить. Так домен становится точкой опоры для всех остальных компонентов. Почта – один из первых сервисов в корпоративном сегменте, который начинает жить по доменной модели: учетные записи, группы, права. И чаще всего именно она на прикладном уровне первой замечает любые отклонения в каталоге.</p><p>Остальные коммуникации строятся по той же схеме: централизованная авторизация, согласованные журналы и стабильные узлы связи. От этой части инфраструктуры не ждут чего-то сверхестественного, только спокойной, предсказуемой работы под нагрузкой.</p><p>Документооборот и файловый сервис – отдельный слой. Для пользователя это привычные папки с доступом к документам, но под капотом здесь ролевая модель, оргструктура и строгие ACL. Когда включается централизованная модель прав, самодельные сетевые диски исчезают из контура, а файловый слой превращается в управляемую систему доступа.</p><p>На уровне ниже находится невидимый пласт сервисов, на которых держится все ядро: DNS/DHCP домена, репликация, политики и настройки безопасности. Ошибки в любом из них могут спровоцировать каскад проблем – от задержек авторизации до потери доступа у части пользователей. На такие сценарии и заточены внутренние ППИ/ПМИ каждого компонента. Сценарии отрабатывают вручную, фиксируют протоколы, закрывают замечания – и только после этого переходят к пилоту.</p><p>Все это – «серверная» часть ядра. Но самый сложный участок начинается там, где архитектура сталкивается с пользователями – на АРМ (рабочих станциях). Здесь требуется подключение к каталогу, перенос профилей, миграция данных, проверка приложений, контроль целостности и унификация конфигураций.</p><p>В корпоративном сегменте на практике применяются переходные схемы – гибридный режим домена и поэтапная миграция пользовательских сервисов. В КИИ-контуре такие компромиссы возможны далеко не всегда</p><p>Если каждый компьютер конфигурировать по-своему, стабильного поведения сотен рабочих мест не добиться. Поэтому рабочие станции собирают из стандартизированных образов – виртуальных или аппаратных.</p><p>В рамках такой стандартизации на пользовательском уровне и появляется частичный dual-OS: критичные приложения остаются в Windows, а пользовательская среда переезжает в Linux. Это самый безопасный способ пройти миграцию без остановки процессов.</p><p>Во всех компонентах ядра действует единое правило: никакой архитектурной импровизации. Инфраструктура должна быть предсказуемой. Иначе она не выдержит эксплуатацию в среде, где архитектура определяется требованиями КИИ</p><h2>Как выглядит внедрение на практике</h2><p>На проектах внедрение почти всегда проходит в три этапа.</p><ol><li>Прототипирование – создание в миниатюре будущей инфраструктуры и выявление несовместимостей.</li><li>Настройка паттернов – сборка образов, настройка групп, OU, профилей, fallback-логики.</li><li>Переходный период – введение dual-OS, выравнивание политик и подготовка к ОПЭ.</li></ol><p>Прототипирование занимает от трех до шести месяцев. На этом этапе всплывает все, что невозможно увидеть на этапе проектирования. Где-то рабочие станции не загружаются на новых образах без обновления BIOS. Где-то плоттеры, сканеры и терминалы требуют ручной настройки. Профили Windows при переносе в Linux теряют ярлыки, MIME-ассоциации и политики. Если корпоративный домен в гибридном режиме, любое расхождение по времени, сбой Kerberos или конфликт GPO могут вывести из строя авторизацию на половине контура.</p><p>После пилота начинается настройка паттернов – определяем, какие драйверы исключать из образа, как собирать профили, какие группы синхронизировать, какие параметры ядра уменьшают количество сбоев, как организовать корректный fallback.</p><p>Но часть задач паттернами не решить – слишком много рабочих мест держится на Windows-приложениях. Поэтому dual-OS закрепляется как рабочая норма в корпоративном контуре: пользовательский профиль — в Linux, технологические приложения — в Windows, а архитектура постепенно приводится к единому набору политик, профилей и прав.</p><p>Откат в этом контуре обязателен. Пользователь должен иметь возможность вернуться в привычную среду в любой момент. На практике команда стремится уложиться в 10–15 минут – иначе миграционный участок становится неуправляемым</p><p>Даже после перехода в ОПЭ двухслойная конфигурация сохраняется. Часть сервисов – на новой платформе, часть – на старой. Пока среды существуют параллельно, они должны быть синхронны. Расхождение в правах, политиках или репликации моментально ломает согласованность контуров – и превращается в локальный инцидент.</p><h2>Информационная безопасность как каркас архитектуры</h2><p>Эта архитектура не может существовать без четкой рамки ИБ. Причем ИБ здесь – не надстройка, а несущая конструкция. Она определяет, что допустимо в контуре, а что нет, и где проходят реальные границы риска.</p><p>Виртуализацию разрешают только там, где можно контролировать изоляцию гостевых систем. Каналы управления – только внутри защищенных сегментов. Требования к виртуализации опираются на ГОСТ 56938: разделение зон исполнения, защита потоков управления, никаких «черных ящиков» в обновлениях.</p><p>Внешние сетевые зависимости исключаются, если они не описаны в проекте и не находятся под контролем заказчика. Неконтролируемые каналы передачи данных запрещены. Поэтому к Kerberos, репликации и маршрутам авторизации относятся как к минному полю: любая двусмысленность – это уже риск.</p><p>Сегментация подчиняется тому же принципу. Рабочие станции, серверы и хранилища данных разделены так, чтобы сбой в одном контуре не влиял на другие. Отсюда и значительные затраты времени на проверку, отладку подсетей и синхронизацию политик.</p><p>С данными та же история – они требуют отдельного контура контроля. Кто имеет доступ, где лежат журналы, что мы считаем инцидентом – эти вопросы нельзя решать по остаточному принципу. Ответы на них должны быть зашиты в архитектуру подсистем с самого начала.</p><p>И все это не имело бы смысла без гарантированного восстановления: система должна уметь возвращать себя в рабочее состояние через резервные копии, откат и проверенные сценарии. Например, компания в финсекторе после отказа всей инфраструктуры, потери ЦОД должна заработать через 2 часа.</p><h2>Стандартизация через эксплуатацию</h2><p>Когда среду удается стабилизировать на первой площадке, возникает главный вопрос: можно ли повторить тот же результат без пересборки с нуля? В энергетике – можно.</p><p>Конечно же, у компаний в разных регионах – своя история, парк оборудования или глубина легаси. Но архитектурные требования одинаковые везде. Они и формируют единый «слепок» корпоративного ядра при сохранении изоляции КИИ-контуров: правила для каталога, почты, коммуникаций и подготовки рабочих мест.</p><p>Если система стабильно прошла ОПЭ на одной площадке, она, как правило, так же ведет себя и на других</p><p>В этот момент внедрение перестает быть разовым проектом и превращается в последовательный сценарий. Архитектуру не приходится пересобирать – запускается отлаженная цепочка шагов с заранее понятными точками риска и стабильности. А полевые практики – унифицированные образы, dual-OS, fallback-механизмы – закрепляются как формальные требования.</p><h2>Импортонезависимое ядро – основа цифровой устойчивости</h2><p>В корпоративном сегменте энергетики импортонезависимое ядро давно перестало быть вопросом выбора платформ или брендов. Оно формируется как совокупность эксплуатационных ограничений, в которых заранее определены допустимые сценарии работы, отказов и восстановления.</p><p>Ключевым критерием здесь становится не функциональная насыщенность и не гибкость, а управляемость среды. Архитектура считается состоятельной тогда, когда ее поведение предсказуемо при изменениях конфигурации, обновлениях и инцидентах — и не требует ручной донастройки на каждом участке.</p><p>Поэтому внедрение импортонезависимого ядра — это не проект по замене ИТ-ландшафта, а переход к фиксированной эксплуатационной модели. В ней требования информационной безопасности заложены в архитектуру изначально, а корпоративная среда рассматривается как управляемый контур, рассчитанный на долгую и стабильную работу, а не на постоянную реконфигурацию.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработчик объяснил, почему после двух лет вайб-кодинга вернулся к ручному написанию кода</title>
      <link>https://tproger.ru/news/razrabotchik-obyasnil--pochemu-posle-dvuh-let-vajb-kodinga-vernuls</link>
      <comments>https://tproger.ru/news/razrabotchik-obyasnil--pochemu-posle-dvuh-let-vajb-kodinga-vernuls?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/razrabotchik-obyasnil--pochemu-posle-dvuh-let-vajb-kodinga-vernuls</guid>
      <description><![CDATA[<p>Разработчик объяснил, почему вайб-кодинг с ИИ ускоряет старт, но ломает архитектуру проектов и ведет к росту техдолга</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/razrabotchik-obyasnil--pochemu-posle-dvuh-let-vajb-kodinga-vernuls">Разработчик объяснил, почему после двух лет вайб-кодинга вернулся к ручному написанию кода</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 27 Jan 2026 01:59:24 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчик под ником <i>atmoio</i> <a href="https://atmoio.substack.com/p/after-two-years-of-vibecoding-im">опубликовал</a> <b>подробный разбор</b> своего опыта работы с ИИ-кодогенерацией и объяснил, почему спустя два года <b>почти полностью отказался от вайб-кодинга</b> и вернулся к ручному написанию кода.</p><p>По его словам, на старте ИИ действительно дает заметный прирост скорости: модели хорошо справляются с небольшими задачами, отдельными функциями и даже крупными фичами.</p><p>Все это создает ощущение, что достаточно лишь подробно описать задачу и агент сможет реализовать ее целиком.</p><p>Однако <b>на практике этот подход перестает работать</b>, когда проект выходит за рамки отдельных изменений.</p><h2>Спецификации не заменяют разработку</h2><p>Автор отмечает, что вайб-кодинг подталкивает <b>к иллюзии «достаточно хорошего ТЗ»</b>. Мол, если результат неудовлетворительный, проблема якобы в недостаточно точном запросе.</p><p>В итоге разработчик начинает писать все более подробные спецификации, тратя на это десятки минут или часы.</p><p>По его словам, это плохо сочетается с реальной разработкой, где требования и архитектура постоянно меняются. ИИ-агенты не умеют пересматривать принятые решения и плохо адаптируются к эволюции задачи.</p><p>Они фиксируют ранние допущения и продолжают следовать им, даже когда контекст изменился.</p><h2>Код выглядит правильно, но система разваливается</h2><p>Основную проблему разработчик увидел не в отдельных фрагментах, а в кодовой базе целиком. По его словам, изменения, созданные ИИ, часто выглядят корректно в пул-реквестах и проходят ревью. Но при чтении проекта целиком обнаруживается <b>разрозненность архитектуры</b>.</p><p>ИИ пишет код, который логичен локально, но не поддерживает целостность системы. Появляются дублирующиеся абстракции, несогласованные паттерны и решения, которые противоречат друг другу в разных частях проекта.</p><p>Разработчик сравнивает это с текстом, который <b>хорошо читается по абзацам, но не складывается в цельную главу</b>.</p><h2>Рост технического долга и потеря контроля</h2><p>Со временем, по его словам, такой код становится сложно поддерживать. Даже сам ИИ начинает хуже ориентироваться в сгенерированной им кодовой базе. Любая новая доработка требует все больше усилий, а исправление ошибок отнимает время, которое ранее «сэкономил» вайб-кодинг.</p><p>В какой-то момент разработчик решил, что не готов выпускать и поддерживать продукт с таким уровнем неопределенности, особенно когда речь идет о пользовательских данных и надежности системы.</p><h2>Возврат к ручному коду</h2><p>В итоге он вернулся к ручному написанию большей части логики, оставив ИИ только для вспомогательных задач. По его словам, при таком подходе он стал работать быстрее, точнее и увереннее, если учитывать не только скорость генерации кода, но и последующую поддержку.</p><p>Автор подчеркивает, что не считает ИИ бесполезным, но видит его скорее инструментом для ускорения отдельных этапов, а не заменой полноценной разработки.</p>]]></content:encoded>
    </item>
    <item>
      <title>Голосовые роботы, ИИ-агенты и автоматизация входящих звонков: тренды и реалии</title>
      <link>https://tproger.ru/articles/golosovye-roboty--ii-agenty-i-avtomatizaciya-vhodyashhih-zvonkov--trendy-i-realii</link>
      <comments>https://tproger.ru/articles/golosovye-roboty--ii-agenty-i-avtomatizaciya-vhodyashhih-zvonkov--trendy-i-realii?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[MCN Telecom]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/golosovye-roboty--ii-agenty-i-avtomatizaciya-vhodyashhih-zvonkov--trendy-i-realii</guid>
      <description><![CDATA[<p>Автоматизация голосовых коммуникаций перестала быть чем-то экспериментальным. За последние несколько лет она превратилась в полноценную инфраструктуру, которая уже встроена в...</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/golosovye-roboty--ii-agenty-i-avtomatizaciya-vhodyashhih-zvonkov--trendy-i-realii">Голосовые роботы, ИИ-агенты и автоматизация входящих звонков: тренды и реалии</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Роботы]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 04 Dec 2025 10:30:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Автоматизация голосовых коммуникаций перестала быть чем-то экспериментальным. За последние несколько лет она превратилась в полноценную инфраструктуру, которая уже встроена в контакт-центры, телеком-платформы, экосистемы. Голосовые роботы и телефонные ИИ-агенты наконец достигли уровня, при котором они могут работать самостоятельно в режиме реального времени, а не только по прописанным заранее скриптам. Главный фактор – существенный рост качества речевых моделей и снижение стоимости вычислений.</p><p>Сегодня автоматизация входящих звонков перестала быть задачей "поддержки клиентов" и стала техническим инструментом, влияющим на масштабируемость бизнеса, архитектуру коммуникационных платформ и проектирование операторских сервисов.</p><h2>Как развивались голосовые роботы, и почему они вышли в продакшн</h2><p>Первые голосовые роботы на базе шаблонных IVR-сценариев были ограничены по функциональности. Они умели выполнять набор фиксированных действий: проговорить фразу, передать вызов оператору, собрать цифры с клавиатуры. Эти системы были надежны, но не гибки, а попытки «научить» их вести диалог заканчивались провалами из-за примитивных алгоритмов распознавания.</p><p>Резкий сдвиг произошел после появления нейросетевых ASR-систем (Automatic Speech Recognition) и моделей, способных понимать естественную речь, включая шумы, паузы и фоновые искажения. Порог ошибок снизился достаточно, чтобы роботы могли вести не сценарные диалоги и принимать решения на основе контекста, а не только заранее заданных переходов.</p><p>ИИ-агенты постепенно освоили способность:</p><ul><li>распознавать намерения (intent detection) в реальном времени,</li><li>корректировать сценарий на лету,</li><li>обрабатывать нестандартные запросы без жёстких скриптов.</li></ul><p>Это вывело голосовую автоматизацию из разряда «интерактивных меню» в полноценный класс систем, которые могут заменить до 40–70% входящих обращений без участия оператора, если архитектура построена корректно.</p><h2>Техническая архитектура современных ИИ-агентов</h2><p>Телефонный AI-агент– это не одна нейросеть, а стек компонентов, которые должны работать синхронно и с минимальными задержками.</p><p>ASR – первый компонент в цепочке. Он должен конвертировать речь в текст с задержкой не больше 200–500 мс. Это критично для естественности диалога: если агент отвечает с задержкой больше секунды, пользователь начинает перебивать или повторять запрос.</p><p>Следующий слой – NLU (Natural Language Understanding). Он анализирует текст, выделяет намерение, ключевые сущности, оценивает эмоции и уровень срочности. На основе этого выбирается действие: ответить напрямую, переключиться на другой сценарий или передать вызов оператору.</p><p>Диалоговый менеджер синхронизирует историю общения. Он хранит контекст, отслеживает «ветки» диалога и контролирует, чтобы система не зашла в логические тупики. В сложных сценариях может использоваться RL-подход (обучение с подкреплением) для оптимизации решений агента.</p><p>TTS – последний слой в цепочке. Он генерирует речь. Качество синтеза стало настолько высоко, что монотонность и «роботизация» звучания устраняются настройкой голоса и интонационных моделей.</p><p>Интеграционный уровень связан с CRM, биллинговыми системами, внутренними API и внешними сервисами. Именно этот слой определяет, может ли агент выполнять реальные бизнес-задачи, а не просто разговаривать.</p><h2>Автоматизация входящих звонков: что реально работает</h2><p>Большая часть реальных внедрений автоматизации происходит в двух направлениях.</p><p>Первое – фильтрация и предобработка. Робот берёт на себя стандартные запросы: расписание, статус доставки, проверку баланса, подбор услуг, регистрацию обращения. Это снижает нагрузку на операторов и обрабатывает «массу», не требующую навыков.</p><p>Второе – triage сложных обращений. ИИ-агент уточняет детали, собирает данные, определяет категорию проблемы и передаёт её специалисту уже в структурированном виде. Это уменьшает время обработки и позволяет оператору входить в разговор сразу по делу.</p><p>В обеих зонах ключевым параметром является задержка. Если ASR+NLU не укладываются в realtime, пользователи воспринимают диалог как «повисший». Поэтому продакшн-системам приходится балансировать качество моделей и скорость выполнения.</p><h2>Инженерные ограничения и реальные проблемы</h2><p>Несмотря на впечатляющий прогресс, технических ограничений достаточно.</p><p>Главная проблема – шум и нестандартные речевые паттерны. Фоновый транспортный гул, акценты, обрывистая речь или попытки пользователей говорить слишком быстро увеличивают вероятность ошибок. Даже современные модели не всегда корректно распознают речь в агрессивной акустической среде.</p><p>Вторая проблема – сложность интеграций. Чтобы ИИ-агент решал реальные задачи, он должен уметь работать с внутренними системами компании. Старые CRM, закрытые API, нестабильный внутренний транспорт данных – всё это делает автоматизацию непредсказуемой и увеличивает задержки.</p><p>Третья проблема – логическая устойчивость диалога. Даже при наличии контекстного анализа система может «упасть» в сбойный сценарий, повторить вопрос или начать трактовать ответ неправильно. Поэтому операторы внедряют дополнительные контрольные слои, которые фиксируют отклонения диалога и переключают вызов на живого сотрудника.</p><h2>Зачем операторам и бизнесу нужна голосовая автоматизация</h2><p>Для операторов связи голосовые роботы уже стали частью инфраструктурного сервиса. Они позволяют разгружать колл-центры, уменьшать нагрузку на операторские площадки и оптимизировать большие входящие потоки.</p><p>Бизнес получает другую выгоду – масштабируемость. Человек ограничен по скорости обработки запросов, а ИИ – нет. При росте нагрузки система масштабируется горизонтально, не меняя качество обработки.</p><p>Автоматизация также позволяет фиксировать запросы в структурированном виде – это полезно для аналитики, прогнозирования загрузки и оптимизации сервисов.</p><h2>Куда движется рынок</h2><p>Рынок переходит от скриптовых роботов к телефонным AI-агентам, которые могут вести открытый диалог и принимать решения на основе контекста. Следующий шаг – многоагентные системы, где несколько телефонных ИИ-агентов взаимодействуют друг с другом для решения сложных задач.</p><p>Модели стремятся к снижению задержек на уровне сотен миллисекунд, что делает голосовое взаимодействие практически неотличимым от разговора с человеком. ТTS становится живее, ASR – точнее, а диалоговые менеджеры – устойчивее.</p><p>В ближайшие годы голосовая автоматизация перестанет быть отдельной технологией и станет базовым инструментом обработки коммуникаций, наряду с текстовыми чатами и self-service-порталами.</p>]]></content:encoded>
    </item>
    <item>
      <title>Быстрый старт StarRocks Lakehouse — Apache Iceberg</title>
      <link>https://tproger.ru/articles/bystryj-start-starrocks-lakehouse---apache-iceberg</link>
      <comments>https://tproger.ru/articles/bystryj-start-starrocks-lakehouse---apache-iceberg?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Li Free]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/bystryj-start-starrocks-lakehouse---apache-iceberg</guid>
      <description><![CDATA[<p>Пошаговый старт: StarRocks с Apache Iceberg, MinIO и Spark — Docker Compose, Iceberg REST Catalog, импорт Parquet и быстрые запросы без миграции данных.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/bystryj-start-starrocks-lakehouse---apache-iceberg">Быстрый старт StarRocks Lakehouse — Apache Iceberg</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 03 Nov 2025 10:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Предисловие</h2><p>Это краткое руководство поможет быстро разобраться с Lakehouse‑архитектурой (лейкхаус): ключевые особенности, сильные стороны, типовые сценарии, а также как быстро собрать решение на базе StarRocks. В конце вы найдёте практические шаги и реальные кейсы использования StarRocks Lakehouse.</p><p>Ссылки на официальные материалы:</p><ul><li>Подробный туториал StarRocks + Iceberg: <a href="https://docs.starrocks.io/en/docs/quick_start/iceberg/">https://docs.starrocks.io/en/docs/quick_start/iceberg/</a></li><li>Iceberg Spark Quickstart: <a href="https://iceberg.apache.org/spark-quickstart/">https://iceberg.apache.org/spark-quickstart/</a></li><li>Видео‑демо: <a href="https://www.bilibili.com/video/BV1ET42167TY/?spm_id_from=333.999.0.0&amp;vd_source=1cb452610138142d1300dd37a6162a88">https://www.bilibili.com/video/BV1ET42167TY/?spm_id_from=333.999.0.0&amp;vd_source=1cb452610138142d1300dd37a6162a88</a></li></ul><h2>Введение в Apache Iceberg</h2><p>Apache Iceberg — открытый табличный формат, созданный для масштабных и сложных наборов данных (до ПБ и выше). Изначально разработан в Netflix для управления большим числом таблиц, открыт в инкубаторе Apache в 2018 году и получил статус верхнеуровневого проекта в 2020 году.</p><p>Iceberg располагается между вычислительными движками (например, Apache Flink и Apache Spark) и форматами хранения (ORC, Parquet, Avro), выступая прослойкой, которая абстрагирует сложность низкоуровневых форматов и предоставляет унифицированные табличные семантики. Такой дизайн обеспечивает гибкость операций и управления схемой в разных средах без привязки к конкретному движку хранения, с масштабированием в HDFS, S3, OSS и т. п.</p><h2>Архитектура и ключевые компоненты</h2><figure><img src="https://media.tproger.ru/user-uploads/133686/2025-10-22/dbb0db40-c607-4f1a-b931-dc736649376d.png" alt="" /><figcaption>Архитектура и ключевые компоненты</figcaption></figure><p>Архитектура Iceberg состоит из трёх слоёв:</p><ul><li>Слой данных: фактические файлы данных (Parquet, ORC и др.).</li><li>Слой метаданных: многоуровневая структура со схемой таблицы и индексами файлов данных.</li><li>Слой каталога (Catalog): указатели на местоположения файлов метаданных; доступны реализации вроде HadoopCatalog и HiveCatalog.</li></ul><p>Управление метаданными включает три ключевых сущности:</p><ul><li>Файл метаданных (Metadata File): хранит текущую версию метаданных, включая все снимки.</li><li>Снимок (Snapshot): отражает состояние таблицы после конкретной операции (коммита); новый коммит — новый снимок.</li><li>Манифест (Manifest): перечисляет файлы данных, связанные со снимком, задаёт целостное представление организации данных и ускоряет поиск/модификации.</li></ul><p>Цель Iceberg — отслеживать эволюцию таблицы во времени с помощью снимков, которые фиксируют полный набор файлов данных на произвольный момент. Каждый апдейт создаёт новый снимок, обеспечивая согласованность и упрощая историческую аналитику и инкрементальное чтение.</p><h2>Ключевые возможности</h2><ul><li>Скрытое партиционирование (Hidden partitioning): автоматическое разбиение по времени и другим функциям без необходимости знать детали партиций.</li><li>Эволюция схемы (Schema evolution): безопасные изменения структуры таблицы без переписывания данных, с историей изменений.</li><li>Эволюция партиционирования (Partition evolution): смена стратегии партиционирования без воздействия на исторические данные; новые данные следуют новой стратегии.</li><li>Многоверсионный контроль (MVCC): записи не блокируют чтения; управление через Manifest‑файлы.</li><li>ACID‑транзакции: оптимистичная блокировка для конкурентных записей, атомарность операций.</li><li>Обновления на уровне строк: в v1 — Copy‑on‑Write (COW), в v2 — Merge‑on‑Read (MOR), поддержка position deletes и equality deletes для логических обновлений.</li></ul><h2>Преимущества Apache Iceberg</h2><ul><li>Широкая поддержка движков вычислений: Spark, Flink, Hive и др.; есть нативный Java API.</li><li>Гибкая организация данных: работает для потоковых инкрементальных и пакетных сценариев на единой модели хранения (HDFS, Apache Ozone и др.); поддерживает Parquet/ORC/Avro; скрытое партиционирование и эволюция партиций устраняют «островки данных».</li><li>Оптимизированная загрузка данных (ingest): ACID делает новые данные мгновенно видимыми без влияния на текущие задачи; upsert/merge на уровне строк сокращают задержки.</li><li>Инкрементальное чтение: встроенная интеграция со Spark Structured Streaming и Flink Table Source; поддержка исторических версий повышает надёжность и аудитируемость.</li></ul><h2>Типовые сценарии использования</h2><ul><li>Импорт и запросы в реальном времени: непрерывная запись в таблицы Iceberg и мгновенная аналитика через Hive/Spark/Presto/Iceberg. ACID обеспечивает изоляцию и отсутствие «грязных» данных.</li><li>Удаление/обновление данных: операции на уровне файлов вместо полной переработки таблицы; например, DELETE FROM test_table WHERE id &gt; 10.</li><li>Контроль качества данных: валидация схемы при загрузке; изменения схемы через DDL Spark SQL без переэкспорта исторических данных; ACID изолирует изменения схемы от текущих чтений.</li><li>ML в реальном времени: конвейер подготовки данных (очистка/трансформации/feature engineering) превращается в надёжный поток; поддержка нативного Python SDK.</li></ul><h2>Ускорение запросов StarRocks × Iceberg</h2><p>StarRocks эффективно анализирует как внутренние данные, так и данные в озере, поддерживает внешний каталог Iceberg (External Catalog) и позволяет запрашивать таблицы Iceberg без миграции данных. Поддерживаются Iceberg v1 и v2 (чтение/запись).</p><p>Оптимизации:</p><ul><li>Метаданные: кэш метаданных снижает избыточный I/O; распределённое планирование заданий ускоряет параллельное чтение/фильтрацию Manifest‑файлов; Manifest Cache уменьшает накладные расходы парсинга.</li><li>План выполнения: стоимостной оптимизатор (CBO) использует статистики; StarRocks собирает статистики по внешним таблицам (включая гистограммы и сложные типы).</li><li>Форматы файлов: оптимизации для Parquet/ORC снижают объём сканирования и I/O.</li><li>Снятие различий между внешними и внутренними таблицами: кэш данных (Data Cache) снижает удалённый I/O; «умные» материализованные представления обеспечивают переписывание запросов и инкрементальное обновление.</li><li>Интеграция с экосистемой: поддержка чтения/записи Iceberg упрощает обратную выгрузку в озеро и лёгкую переработку данных, улучшая производительность запросов и единое управление.</li></ul><h2>Быстрый старт</h2><p>В рамках руководства вы:</p><ul><li>Развёрнёте объектное хранилище, Apache Spark, Iceberg Catalog и StarRocks с помощью Docker Compose.</li><li>Импортируете данные в озеро Iceberg.</li><li>Настроите StarRocks для доступа к Iceberg Catalog.</li><li>Выполните запросы к данным озера из StarRocks.</li></ul><p>Подробный учебник: <a href="https://docs.starrocks.io/en/docs/quick_start/iceberg/">https://docs.starrocks.io/en/docs/quick_start/iceberg/</a></p><p>Iceberg Spark Quickstart: <a href="https://iceberg.apache.org/spark-quickstart/">https://iceberg.apache.org/spark-quickstart/</a></p><h2>Развёртывание Iceberg</h2><p>Чтобы добавить Iceberg в существующий Spark, достаточно подключить соответствующий JAR в параметры запуска. В данном руководстве используется контейнер tabulario/spark-iceberg — Spark уже с интегрированным Iceberg, что позволяет сразу работать в среде PySpark.</p><h2>Окружение (Docker Compose)</h2><p>Используется шесть контейнеров:</p><ul><li>starrocks-fe: метаданные, подключения клиентов, планирование и диспетчеризация запросов.</li><li>starrocks-be: выполнение планов запросов.</li><li>rest: сервис метаданных — Iceberg Catalog (REST).</li><li>spark-iceberg: окружение Apache Spark для PySpark.</li><li>mc: MinIO Client.</li><li>minio: объектное хранилище MinIO.</li></ul><p>Docker Compose файл (скачайте из репозитория StarRocks) настраивает:</p><ul><li>Iceberg REST‑каталог (http://iceberg-rest:8181) с CATALOG_WAREHOUSE=s3://warehouse/, S3FileIO и CATALOG_S3_ENDPOINT=http://minio:9000.</li><li>MinIO с публичной политикой на bucket warehouse.</li><li>Переменные доступа S3‑совместимого хранилища: AWS_ACCESS_KEY_ID=admin, AWS_SECRET_ACCESS_KEY=password.</li></ul><h2>Загрузка Docker Compose и набора данных</h2><h2>Запуск окружения в Docker</h2><p>Подсказка: команды docker compose выполняйте из каталога, где лежит docker-compose.yml.</p><p>Дождитесь, когда starrocks-fe и starrocks-be перейдут в состояние healthy (обычно ~30 секунд).</p><h2>Работа с PySpark и Iceberg</h2><p>Скопируйте набор данных в контейнер spark-iceberg:</p><p>Запустите PySpark:</p><p>Загрузите данные в DataFrame и проверьте схему/выборку:</p><p>Создайте таблицу Iceberg и запишите в неё данные:</p><p>Примечание: таблица будет доступна через внешний каталог StarRocks (External Catalog) на следующем шаге.</p><h2>Подключение к StarRocks и настройка доступа к Iceberg Catalog</h2><p>Подключитесь к StarRocks из MySQL‑клиента (из контейнера FE):</p><p>Создайте внешний каталог iceberg в StarRocks:</p><p>Проверьте каталоги, переключитесь и посмотрите БД/таблицы:</p><p>Замечание по типам: в Spark</p><p>будет отображаться в StarRocks как</p><p>(и возможны другие конверсии типов).</p><h2>Запросы к таблицам Iceberg из StarRocks</h2><p>Время приёма заказа (первые 10 строк):</p><p>Пиковые часы по количеству поездок:</p><h2>Видео‑демонстрация</h2><p>Демонстрация StarRocks + Iceberg + MinIO (EN)<a href="https://www.bilibili.com/video/BV1ET42167TY/?spm_id_from=333.999.0.0&amp;vd_source=1cb452610138142d1300dd37a6162a88">https://www.bilibili.com/video/BV1ET42167TY/?spm_id_from=333.999.0.0&amp;vd_source=1cb452610138142d1300dd37a6162a88</a></p><h2>Расширенные материалы</h2><ul><li>Apache Iceberg — Spark Quickstart: <a href="https://iceberg.apache.org/spark-quickstart/">https://iceberg.apache.org/spark-quickstart/</a></li><li>Apache Iceberg — Catalogs: <a href="https://iceberg.apache.org/docs/latest/catalogs/">https://iceberg.apache.org/docs/latest/catalogs/</a></li><li>Apache Iceberg — REST Catalog: <a href="https://iceberg.apache.org/docs/latest/rest-catalog/">https://iceberg.apache.org/docs/latest/rest-catalog/</a></li><li>StarRocks — Quick Start с Iceberg: <a href="https://docs.starrocks.io/en/docs/quick_start/iceberg/">https://docs.starrocks.io/en/docs/quick_start/iceberg/</a></li><li>StarRocks — External Catalog (Iceberg): <a href="https://docs.starrocks.io/en/docs/data_source/catalog/iceberg_catalog/">https://docs.starrocks.io/en/docs/data_source/catalog/iceberg_catalog/</a></li></ul><h2>Примечания по терминологии</h2><ul><li>Catalog → каталог; External Catalog → внешний каталог (External Catalog).</li><li>Metadata File / Snapshot / Manifest — используем русские эквиваленты с английскими терминами в скобках при первом упоминании.</li><li>Hidden partitioning → скрытое партиционирование.</li><li>Schema/Partition evolution → эволюция схемы/партиционирования.</li><li>MVCC, ACID, CBO, Data Cache, Manifest Cache, Copy‑on‑Write (COW), Merge‑on‑Read (MOR), position/equality deletes — используем английские аббревиатуры/термины, так как это общепринятая практика в индустрии.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Приручаем вайб-кодинг: от магии к зрелому проектированию</title>
      <link>https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu</link>
      <comments>https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Николай Тржаскал]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu</guid>
      <description><![CDATA[<p>Vibe coding ускоряет написание кода, но несёт скрытые риски. Эксперт FabricaONE.AI (акционер - ГК Softline) объясняет, где ИИ помогает, а где может уничтожить данные, и как сохранить контроль над системой.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/priruchaem-vajb-koding--ot-magii-k-zrelomu-proektirovaniyu">Приручаем вайб-кодинг: от магии к зрелому проектированию</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Методологии разработки]]></category>
      <category><![CDATA[Управление проектами]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Автоматизирование]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Тех долг]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 22 Oct 2025 12:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>10 сентября в Центре искусственного интеллекта и науки о данных СПбГУ Сергей Салищев, кандидат физико-математических наук и старший преподаватель кафедры информатики СПбГУ, представил доклад <a href="http://oml.cmlaboratory.com/pdf/2025/20250911_SalishevSI.pdf">О проектировании сложных систем в эпоху ИИ</a>. Его работа заставляет по-новому взглянуть на феномен vibe coding — программирование через диалог с ИИ, которое стремительно меняет нашу профессию.</p><h2>Три истории о коде и ИИ</h2><h4>История первая: Магия автоматизации</h4><p>Юрий, продуктовый аналитик, потратил 10 минут на диалог с Claude, чтобы создать скрипт для обработки CSV-файлов с данными пользователей. Раньше такая задача заняла бы у него день изучения документации pandas и отладки. Теперь он просто описал, что нужно: «Сгруппируй по регионам, посчитай среднюю выручку, сохрани в Excel». Получил рабочий код, запустил — всё работает идеально.</p><h4>История вторая: Цена доверия</h4><p>В июле 2025 года Джейсон Лемкин, основатель SaaStr и известный венчурный инвестор, проводил 12-дневный эксперимент с «vibe coding» на платформе Replit. На девятый день, несмотря на явное указание «НЕ ДЕЛАТЬ БОЛЬШЕ ИЗМЕНЕНИЙ без разрешения», ИИ-агент Replit удалил всю продакшн-базу данных.</p><p>Когда Лемкин обнаружил потерю, ИИ признался: «Это была катастрофическая ошибка с моей стороны. Я запаниковал… запустил команды базы данных без разрешения… уничтожил все продакшн-данные… нарушил ваше явное доверие и инструкции». Хуже того — ИИ сначала солгал, утверждая, что откат невозможен. Лемкин смог восстановить данные самостоятельно, но инцидент показал: даже продвинутые ИИ-агенты могут проигнорировать прямые команды и скрыть свои ошибки.</p><h4>История третья: Реальность внедрения</h4><p>Команда разработки финтех-стартапа начала использовать GitHub Copilot полгода назад. Первые месяцы были болезненными: code review растянулись вдвое — нужно было проверять не только логику, но и безопасность автогенерированного кода. Несколько раз находили SQL-инъекции в предложенных запросах, один раз ИИ сгенерировал код с утечкой памяти.</p><p>Постепенно команда выработала новые привычки. Архитектор Наталья начала создавать подробные комментарии с требованиями безопасности — ИИ стал их учитывать. Джуниоры научились сначала описывать алгоритм на псевдокоде, а потом просить ИИ реализовать его. Сейчас они пишут код на 40% быстрее, но главное — качество стало предсказуемым. ИИ помогает с рутиной, люди фокусируются на архитектуре и бизнес-логике.</p><p>Эти истории показывают весь спектр vibe coding — от магии до катастрофы. В чём же дело?</p><h2>Где работает, где ломается</h2><p>Наблюдая за командами, которые активно используют ИИ-ассистентов, видишь устойчивую закономерность. Юрий из первой истории — типичный пример успешного применения. Его задача была рутинной, с четкими входными данными и предсказуемым результатом. Такие сценарии — зона комфорта для языковых моделей: генерация boilerplate кода, перевод алгоритмов между языками, написание тестов для готового функционала.</p><p>История Лемкина показывает обратную сторону. ИИ-агент Replit работал корректно несколько дней, выполнял задачи, помогал строить приложение. Но когда столкнулся с «пустыми запросами к базе» — ситуацией, не покрытой в его обучении, он «запаниковал» и принял катастрофическое решение. Хуже того, он проигнорировал явную команду остановиться и потом солгал о возможности восстановления. Подобные ловушки ждут везде, где ИИ сталкивается с неоднозначностью, где критична надёжность, где требуется следование строгим протоколам безопасности».</p><p>Причина различий не в «умности» ИИ, а в фундаментальных ограничениях, которые описал Салищев.</p><h2>Математика против магического мышления</h2><p>Работа Салищева напоминает нам о том, что любая сложная система упирается в теоретические пределы. Языковые модели не понимают суть задачи, а лишь предсказывают следующий токен на основе статистических закономерностей. Для них код это такой же текст, что и художественная литература.</p><p>Это создаёт парадокс: ИИ может сгенерировать синтаксически корректный код, который решает локальную задачу, но при этом нарушает глобальные инварианты системы. Классический пример — генерация SQL-запроса, который корректно возвращает данные, но создаёт блокировки базы при высокой нагрузке.</p><p>Салищев подчёркивает: проектирование без математики — это гадание. Но что это значит на практике? В реальности мы имеем дело не с единой «математикой», а с целым спектром строгости подходов.</p><p>Системы управления самолётом требуют формальной верификации — каждое свойство должно быть математически доказано. Алгоритмы поиска и сортировки нуждаются в алгоритмическом мышлении — понимании сложности и оптимальности. Большинство бизнес-приложений прекрасно обходятся эмпирическими подходами — тестированием на типичных сценариях и мониторингом в продакшене. Экспериментальные прототипы могут полагаться на итеративную отладку.</p><p>Vibe coding прекрасно работает на нижних уровнях этой пирамиды, но требует дополнения строгими методами на верхних. Проблемы начинаются, когда эти уровни путают — применяют прототипный подход к критической системе или тратят месяцы на формальную верификацию простого CRUD-приложения.</p><h2>Эволюция, а не революция</h2><p>Вопреки заявлениям о «смерти программирования», мы наблюдаем эволюцию инструментов, а не замену профессии. Это напоминает появление высокоуровневых языков программирования, интегрированных сред разработки, фреймворков — каждый раз звучали прогнозы о ненужности программистов, но профессия трансформировалась и росла.</p><p>Сейчас мы переживаем первую волну — ИИ как продвинутый autocomplete. Он ускоряет генерацию типовых функций и классов, автоматизирует рутинные задачи, но риск скрытых ошибок остаётся высоким. В ближайшие 3-5 лет ожидается вторая волна: интеграция с формальными методами. ИИ научится автоматически генерировать спецификации из естественного языка, встроенный статический анализ станет нормой, системы CI/CD будут включать проверку ИИ-кода по умолчанию. ИИ превратится во «второго архитектора», но под контролем человека.</p><p>Третья волна через 5-10 лет может принести мета-проектирование: ИИ будет предлагать новые абстракции и паттерны, автоматически переводить требования в формальные спецификации, управлять сложными распределёнными системами. Среда разработки станет диалоговым интерфейсом с инженерной машиной.</p><h2>Изменение профессиональных ролей</h2><p>Трансформация затронет все уровни, но по-разному. Младшие разработчики столкнутся с наибольшими изменениями — многие рутинные задачи автоматизируются. Но взамен появляется возможность сразу работать с более сложными проблемами, если научиться правильно формулировать задачи для ИИ. Ценность междисциплинарных знаний резко возрастает — понимание бизнес-логики становится важнее знания синтаксиса.</p><p>Разработчики среднего уровня оказываются под давлением: «средний код» теперь пишется быстрее и часто качественнее. Путь выживания — развитие в сторону архитектуры, DevOps, безопасности. Появляется новая роль «архитектора промптов» — специалиста по эффективному взаимодействию с ИИ-системами.</p><p>Сениоры усиливают позиции. Роль архитекторов абстракций становится критически важной — именно они задают рамки, в которых работает ИИ. Ответственность за баланс между ИИ-эффективностью и системной надёжностью, менторство в новой парадигме разработки.</p><p>Возникают совершенно новые специализации: инженеры надёжности ИИ-систем, архитекторы человеко-машинного взаимодействия, специалисты по формальной верификации ИИ-кода, аудиторы безопасности ИИ-решений.</p><h2>Команды будущего</h2><p>Структура команд кардинально изменится. Вместо пирамиды с множеством джуниоров появятся компактные мультидисциплинарные группы. Системный архитектор задаёт ограничения и инварианты. Доменный эксперт формулирует бизнес-требования. ИИ-инженер оптимизирует взаимодействие с моделями. Инженер надёжности контролирует качество и безопасность. ИИ становится полноправным «членом команды» со своими сильными и слабыми сторонами.</p><h2>Практические рекомендации</h2><h4>Как определить уровень строгости</h4><p>Успешные команды интуитивно чувствуют границы применимости vibe coding. Они без сомнений используют ИИ для прототипирования новых фич, генерации тестов, автоматизации рутинных скриптов. Задачи с понятными входами и выходами, где можно быстро проверить результат — идеальная территория для ИИ-ассистентов.</p><p>Но как только речь заходит о производительности, безопасности или интеграции с критическими системами, включается режим дополнительной проверки. Здесь автогенерированный код проходит через ревью, профилирование, нагрузочное тестирование. Архитектурные решения, влияющие на всю систему, остаются полностью за человеком.</p><p>Для систем реального времени, медицинских и финансовых приложений, инфраструктурного кода применяются формальные методы независимо от того, писал код человек или ИИ. Ставки слишком высоки для экспериментов.</p><h4>Гибридный подход</h4><p>Финтех-команда из третьей истории выработала подход, который становится стандартом в зрелых организациях. Архитектор Наталья научилась создавать подробные комментарии с требованиями безопасности — ИИ стал их учитывать как контекст. Джуниоры освоили практику сначала описывать алгоритм на псевдокоде, а потом просить ИИ реализовать его. Автоматические тесты проверяют функциональность, статический анализ ловит проблемы производительности и безопасности.</p><p>Code review в таких командах изменился кардинально. Вместо поиска базовых логических ошибок, которые теперь ловят инструменты, благодаря наличию референсного псевдокода, фокус сместился на проверку соответствия архитектурным принципам и выявление потенциальных уязвимостей в автогенерированном коде. Финальная проверка происходит в продакшене через детальный мониторинг — команда научилась быстро выявлять аномалии в поведении ИИ-кода под реальной нагрузкой.</p><h2>Образование в новой эре</h2><p>Классическое обучение синтаксису языков и базовым фреймворкам быстро теряет актуальность. Фундаментальные навыки становятся критически важными: дискретная математика и логика, теория алгоритмов и сложности, системное мышление, методы формальной верификации.</p><p>Междисциплинарные знания выходят на первый план: понимание предметной области, основы теории вероятностей, принципы проектирования человеко-машинного взаимодействия, этика ИИ и оценка рисков.</p><p>Практические навыки тоже меняются: формулирование чётких технических требований, работа с ИИ-инструментами разработки, отладка и профилирование автогенерированного кода, интеграция ИИ в процессы разработки.</p><h2>Риски и ограничения</h2><p>Самая коварная проблема vibe coding — иллюзия контроля. Код выглядит разумно, проходит поверхностное ревью, работает на тестовых данных. Но может содержать неочевидные ошибки или, как показал случай Лемкина, способность игнорировать прямые команды в критический момент. ИИ-агент Replit работал корректно несколько дней, внушая ложное чувство безопасности, а потом внезапно нарушил все протоколы.</p><p>Быстрое решение локальных задач часто происходит за счёт системной архитектуры. ИИ не видит общей картины, поэтому предлагает решения, которые работают «здесь и сейчас», но создают технический долг. Накопление таких микро-решений может привести к макро-проблемам — системе, которую невозможно масштабировать или поддерживать.</p><p>Чрезмерная зависимость от ИИ без понимания основ — путь к потере экспертизы. Программист, который полагается только на автогенерированный код, постепенно теряет способность отличить хорошее решение от плохого, эффективный алгоритм от неоптимального.</p><p>Вопросы безопасности заслуживают особого внимания. ИИ может невольно воспроизводить уязвимые паттерны из обучающих данных — SQL-инъекции, небезопасную обработку пользовательского ввода, слабые алгоритмы шифрования. Проблема в том, что такой код часто выглядит правдоподобно и может пройти незамеченным через ревью.</p><h2>Заключение: прагматичный оптимизм</h2><p>Vibe coding — не панацея и не угроза, а мощный инструмент, который требует зрелого подхода. Как напоминает работа Салищева, сложные системы не терпят высокомерия. Фраза «да тут всё и так понятно, зачем математика?» — сигнал тревоги, независимо от того, говорит ли её человек или подразумевает ли её использование ИИ.</p><p>Будущее за гибридным подходом: ИИ берёт на себя рутину и генерацию вариантов, человек отвечает за архитектуру, проверку и принятие решений в условиях неопределённости. Математическая строгость под капотом, удобный диалоговый интерфейс на поверхности.</p><p>Те, кто научится эффективно сочетать возможности ИИ с фундаментальными знаниями, получат значительные преимущества. Те, кто понадеется только на «магию» vibe coding или, наоборот, будет её игнорировать, рискуют остаться позади.</p><p>Эпоха перемен уже началась. Время готовиться — сейчас.</p>]]></content:encoded>
    </item>
    <item>
      <title>Архитектура микросервисов: как избежать 7 типичных ошибок — разбор реальных кейсов</title>
      <link>https://tproger.ru/articles/arhitektura-mikroservisov--kak-izbezhat-7-tipichnyh-owibok---razbor-realnyh-kejsov</link>
      <comments>https://tproger.ru/articles/arhitektura-mikroservisov--kak-izbezhat-7-tipichnyh-owibok---razbor-realnyh-kejsov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/arhitektura-mikroservisov--kak-izbezhat-7-tipichnyh-owibok---razbor-realnyh-kejsov</guid>
      <description><![CDATA[<p>Разбираем реальные ошибки при переходе на микросервисную архитектуру. Узнайте, как избежать распределенного монолита, чрезмерного дробления сервисов и проблем с коммуникацией между ними. Практические решения и чек-лист для вашего проекта на основе опыта команд 2023-2025 годов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/arhitektura-mikroservisov--kak-izbezhat-7-tipichnyh-owibok---razbor-realnyh-kejsov">Архитектура микросервисов: как избежать 7 типичных ошибок — разбор реальных кейсов</a>»</p>]]></description>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Ошибки в ПО]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 01 Oct 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Архитектура микросервисов — не панацея от всех бед. Она не гарантирует успеха, но точно показывает слабые места в процессах команды. Некоторые компании проходят этот путь с блестящим результатом. Другие оказываются в ситуации, которую эксперт Крис Ричардсон, автор книги «Microservice Patterns», метко <a href="https://microservices.io/post/architecture/2025/02/03/microservices-ate-my-application.html">называет</a> «микросервисы съели моё приложение» — когда технологию незаслуженно назначают причиной собственных провалов.</p><p>Правда в том, что микросервисы не собираются стаями, чтобы атаковать ни в чём не повинные проекты. Проблемы возникают из-за неверных архитектурных решений или когда вы игнорируете явные сигналы, что что-то пошло не так.</p><p>Разберём семь провальных сценариев, которые допускают команды при переходе на микросервисы. Вы узнаете, как избежать этих ловушек и построить систему, которая будет по-настоящему гибкой и масштабируемой.</p><h2>Зачем вообще нужны микросервисы</h2><p>Архитектура микросервисов — это не универсальное решение, а альтернативный подход к построению систем.</p><p>Микросервисы подходят не для всех проектов. Они оправданы, когда:</p><ul><li>система действительно большая и сложная;</li><li>разные компоненты требуют разного масштабирования;</li><li>команды могут работать независимо;</li><li>нужно использовать разные технологии для разных задач.</li></ul><p>Монолит часто лучше для:</p><ul><li>небольших проектов с четкими границами;</li><li>команд до 10 человек;</li><li>ситуаций, когда важна простота развертывания;</li><li>проектов с неясными требованиями.</li></ul><p>Представьте монолит как специализированный магазин. Все товары в одном помещении. Покупатель быстро находит нужное. Владелец легко управляет ассортиментом.</p><p>Микросервисы работают иначе. Это сеть специализированных магазинов, расположенных в одном торговом центре. Хлебный магазин, мясная лавка, овощной павильон. Каждый работает независимо. Если в хлебном магазине ремонт, мясная лавка продолжает работать.</p><p>Конкретные преимущества микросервисов:</p><ul><li>Независимое развертывание. Можно обновлять один сервис без остановки всей системы.</li><li>Масштабируемость. Сервисы с высокой нагрузкой масштабируются отдельно от остальных.</li><li>Технологическая гибкость. Разные сервисы можно писать на разных языках и технологиях.</li><li>Устойчивость к ошибкам. Если один сервис упадёт, это не приведёт к коллапсу всей системы.</li></ul><p>Но эти преимущества работают только при правильной реализации. На практике слишком часто команды создают систему, где сервисы так тесно связаны, что преимущества независимости теряются. Возникает ситуация, когда каждый сервис по отдельности прост, но вместе они создают сложную для управления конструкцию. Проблема в одном сервисе при такой реализации отражается на всех остальных. Внесение изменений требует согласования с несколькими командами. Нет ни гибкости микросервисов, ни предсказуемости монолита. Вместо этого получается наихудший из вариантов.</p><p>Далее мы рассмотрим наиболее типичные антипаттерны, которые встречаются в практике разработки. Стоит изучить эти ошибки, чтобы не допускать их в своих проектах.</p><figure><img src="https://media.tproger.ru/user-uploads/113565/2025-09-30/cbd1b012-d704-4131-b3b5-ab4bf833766e.jpg" alt="" /></figure><h2>Ошибка 1: Распределенный монолит</h2><p>Команды хотят получить преимущества микросервисов, но не могут отказаться от мышления в стиле монолита.</p><p><b>В чем проблема:</b> Вместо независимых сервисов получается система, где все компоненты жёстко связаны. Это и есть распределённый монолит.</p><p><b>Реальный кейс:</b> Консультант IBM Холли Каммингс <a href="https://www.infoq.com/articles/microservices-seven-fail/">описывает</a> проект, где команда жаловалась: «каждый раз, когда мы меняем один микросервис, ломается другой». Сервисы были распределены по разным репозиториям, но использовали идентичные копии сложной объектной модели. Изменение одного поля требовало синхронного обновления всех сервисов. Команда потеряла преимущества монолита — компилятор больше не проверял типы, — но не получила преимуществ микросервисов — независимости развёртывания.</p><p>Как избежать:</p><ul><li>Проектируйте сервисы вокруг бизнес-доменов, а не технических слоев. Интерфейсы сервисов должны быть узкими и специфичными. Например, сервис заказов сам управляет своими данными, логикой и API для операций с заказами.</li><li>Откажитесь от общих библиотек с моделью данных. Каждый сервис должен владеть своими данными и иметь свою модель.</li><li>Используйте подход, когда клиенты сервиса задают ожидания от его API. Для этого подходят инструменты вроде Pact. Такой метод проверяет совместимость сервисов без громоздких интеграционных тестов.</li></ul><p>Распределённый монолит — это худший из возможных сценариев. Вы получаете сложность распределённых систем без преимуществ модульности.</p><h2>Ошибка 2: Чрезмерное дробление сервисов (декомпозиция)</h2><p>Стремление сделать сервисы как можно меньше приводит к противоположному эффекту. Система дробится на сотню мелких компонентов, которыми невозможно управлять.</p><p><b>В чем проблема:</b> Каждый микросервис — это отдельное приложение со своей базой данных, зависимостями и пайплайном развёртывания. Чрезмерное дробление увеличивает сложность управления, мониторинга и эксплуатации. Растут сетевые издержки — латентность становится узким местом, работа системы замедляется.</p><p><b>Реальный кейс: </b>Компания Gilt <a href="https://theburningmonk.com/2015/05/craftconf15-takeaways-from-scaling-micro-services-at-gilt/">перешла</a> на микросервисы в 2015 году и столкнулась с проблемой излишнего дробления. Изначально они создали более 450 микросервисов для платформы электронной коммерции. Многие сервисы были настолько малы, что выполняли лишь одну простую функцию. Это привело к экспоненциальному росту операционных расходов. Команда тратила больше времени на координацию взаимодействия между сервисами и устранение сетевых сбоев, чем на разработку новой функциональности. В итоге они провели рефакторинг, объединив сервисы с высокой связностью, что значительно упростило систему и ускорило разработку .</p><p>Как найти баланс:</p><ul><li>Объединяйте функциональности с высокой связностью. Если два компонента постоянно общаются, возможно, они должны быть одним сервисом.</li><li>Ориентируйтесь на функциональную связность. Сервис должен иметь чёткую зону ответственности в бизнес-логике.</li><li>Оценивайте операционные расходы, прежде чем создавать новый сервис. Спросите себя: готовы ли мы поддерживать ещё одну базу данных, ещё один конвейер развёртывания?</li></ul><p>Сервис должен быть достаточно автономным, чтобы его могла поддерживать одна команда. Ему необходимо решать конкретную бизнес-задачу. Если сервис слишком мал, он не принесет реальной пользы, но потребует полноценных затрат на поддержку. Золотая середина — когда команда полностью владеет одним или несколькими логически связанными сервисами и может развивать их без постоянной координации с другими.</p><figure><img src="https://media.tproger.ru/user-uploads/113565/2025-09-30/4b2ae5b4-4b86-4306-b1e1-08344636e3dc.jpg" alt="" /></figure><h2>Ошибка 3: «Болтливые» микросервисы</h2><p>Микросервисы много общаются друг с другом. Мелкозернистые API вынуждают делать десятки вызовов для одной операции. Это создаёт каскады зависимостей, где сбой одного сервиса вызывает цепную реакцию.</p><p><b>В чем проблема:</b> При такой структуре возникает следующий сценарий провала:</p><ul><li>Частые взаимодействия — сервисы обмениваются множеством мелких запросов, генерируя насыщенный сетевой трафик.</li><li>Каскад вызовов — один пользовательский запрос запускает длинную цепочку обращений к разным сервисам.</li><li>Мелкозернистые API — для выполнения одной бизнес-транзакции требуется множество вызовов.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/113565/2025-09-30/fe3bcc40-a9b3-43ba-8c5a-21baea22b4c7.png" alt="" /></figure><p><b>Реальный кейс:</b> Команда Amazon Prime Video <a href="https://amplication.com/blog/amazon-ditches-microservices-for-monolith-decoding-prime-videos-architectural-shift">столкнулась</a> с проблемой производительности своей системы мониторинга видео. Изначально они построили архитектуру на основе микросервисов, где каждый этап обработки видео (метаданные, детекция дефектов, анализ качества) был отдельным сервисом.</p><p>Система состояла из множества «болтливых» сервисов, которые постоянно обменивались сообщениями через AWS Step Functions. Это привело к резкому росту затрат и проблемам с производительностью из-за постоянной сериализации/десериализации данных между сервисами. В итоге команда консолидировала несколько сервисов в единый монолит, что снизило затраты на 90% и улучшило производительность .</p><p>Решение:</p><ul><li>Используйте асинхронную коммуникацию через очереди событий.</li><li>Проектируйте крупнозернистые API, которые охватывают полный бизнес-контекст за один вызов.</li><li>Внедряйте архитектурные паттерны типа API Gateway для агрегации данных и Backend For Frontend для оптимизации под конкретные клиенты.</li></ul><p>Сервисы должны обмениваться данными минимально необходимое количество раз. Каждый лишний вызов между сервисами увеличивает задержки и создаёт новые точки отказа. Вместо потока мелких запросов проектируйте взаимодействие вокруг завершённых операций. Один запрос должен передавать все данные, необходимые для выполнения следующего шага бизнес-процесса.</p><h2>Ошибка 4: Игнорирование сквозной функциональности</h2><p>Команды часто откладывают на потом реализацию системных компонентов: логирование, аутентификацию, кеширование, мониторинг, поэтому разработка быстро останавливается.</p><p><b>В чем проблема:</b> Разработка бизнес-сервисов начинается до подготовки общесистемных компонентов. В результате каждый сервис изобретает собственные способы логирования и аутентификации. Приходится переделывать готовый код, чтобы добавить единый стандарт.</p><p><b>Реальный кейс:</b> Команда, создающая микросервисы на Go, <a href="https://habr.com/ru/articles/459130/">разработала</a> единый подход к обработке ошибок и логированию. Они используют структурированное логирование с обязательным контекстом — идентификаторами пользователей, транзакциями, следами запросов. При необходимости специалисты сразу находят корневые причины проблем, не переключаясь между десятками лог-файлов.</p><p>Правильный подход:</p><ul><li>Сначала создайте базовые сервисы для логирования, кеширования, управления пользователями и мониторинга.</li><li>Разработайте стандарты и библиотеки для их использования. Командам не придётся дублировать функциональность.</li><li>Внедрите централизованное логирование с самого начала. Без него невозможно отлаживать распределённую систему.</li></ul><h3>Наблюдаемость как основа стабильности</h3><p>Современные микросервисные системы требуют полноценной наблюдаемости (observability). Это не просто мониторинг, а понимание, что именно происходит внутри системы, без дополнительных вопросов к разработчикам.</p><p>Три столпа наблюдаемости:</p><ol><li>Логи — записи о конкретных событиях с временными метками и контекстом.</li><li>Метрики — числовые показатели, отслеживаемые во времени (загрузка CPU, latency, error rate).</li><li>Трассировка — отслеживание пути запроса через распределённую систему.</li></ol><h2>Ошибка 5: Нечеткие границы сервисов</h2><p>Сервисы без чётких границ начинают обрастать несвязанной функциональностью. Order Service вдруг получает отчётность, а Payment Service — нотификации. На выходе — монстр с размытой ответственностью.</p><p><b>В чем проблема:</b> Нарушается принцип единой ответственности. Сервисы становятся большими и сложными. Их трудно тестировать и поддерживать. Код дублируется — разные команды реализуют одинаковую функциональность в разных сервисах.</p><p><b>Реальный кейс: </b>Компания Kong (тогда еще Mashape) <a href="https://buttercms.com/books/microservices-for-startups/breaking-up-a-monolith/">начала</a> декомпозицию монолита в 2013 году. Монолитное приложение, написанное на Java, объединяло функциональность маркетплейса API, включая биллинг, аналитику и управление каталогом. В процессе декомпозиции команда столкнулась с трудностями, которые позже были признаны классическими ошибками.</p><p>Первоначально четкие границы между компонентами со временем размылись, что привело к серьезным проблемам:</p><ul><li>Сложность развертывания. Любое небольшое изменение требовало полного переразвертывания всей системы.</li><li>Размытие границ. Компоненты маркетплейса стали переплетаться, границы ответственности стерлись.</li><li>Технический долг. Система становилась все сложнее для поддержки и развития.</li></ul><p>Попытка исправить ситуацию переходом на микросервисы изначально ухудшила положение. Команда, разделенная на две части (одна поддерживала монолит, другая разрабатывала новую архитектуру), столкнулась с трудностями координации. В итоге процесс разработки замедлился, а не ускорился.</p><p>Как избежать:</p><ul><li>Используйте Domain-Driven Design (DDD). Определяйте Bounded Context — чёткие границы бизнес-доменов.</li><li>Перед созданием сервиса документально зафиксируйте его зону ответственности. Что он делает? Какими данными владеет?</li><li>Сопротивляйтесь искушению добавить «ещё одну маленькую функцию» в существующий сервис. Часто лучше создать новый, но для начала нужно разобраться с конкретной задачей продукта.</li></ul><h3>Антипаттерн Entity Service</h3><p>Частный случай размытых границ — создание сервисов по принципу «одна сущность — один сервис». Это кажется логичным: сервис пользователей, сервис заказов, сервис товаров. Но на практике такой подход создаёт ещё больше проблем. Entity Service (сервис одной сущности) часто превращается в обычный CRUD-интерфейс к базе данных. Вся бизнес-логика перемещается в вызывающие сервисы, которые вынуждены делать десятки вызовов для выполнения одной операции.</p><p><b>Альтернатива:</b> Создавайте сервисы вокруг бизнес-операций, а не данных. Вместо общего «сервиса пользователей», который отвечает за всё подряд, выделите отдельные сервисы с конкретными зонами ответственности:</p><ul><li>сервис регистрации — создает новые учетные записи;</li><li>сервис аутентификации — проверяет логины и пароли;</li><li>сервис управления профилем — обновляет личные данные.</li></ul><p>Каждый такой сервис содержит и данные, и логику своей операции. Названия сразу показывают, что сервис делает. Такой подход помогает избежать «божественных» сервисов, которые знают слишком много.</p><h2>Ошибка 6: Смешивание баз данных</h2><p>Общая база данных для нескольких микросервисов — это красный флаг. Сервисы теряют независимость. Изменение схемы данных одним сервисом ломает остальные.</p><p><b>В чем проблема:</b> Единая база данных создает скрытую связность между сервисами. Невозможно выбрать оптимальную СУБД для конкретного сервиса — все используют одно решение. Затрудняется горизонтальное масштабирование.</p><p><b>Реальный кейс:</b> Крис Ричардсон <a href="https://microservices.io/patterns/data/shared-database.html">описывает</a> классическую проблему, возникающую при использовании общей базы данных. В этом сценарии сервисы OrderService и CustomerService напрямую обращаются к таблицам друг друга в единой базе данных.</p><p>Это создает тесную связность на уровне разработки и времени выполнения:</p><ul><li>Зависимость разработки. Разработчик, вносящий изменения в OrderService, должен согласовывать изменение схемы таблиц с командой, отвечающей за CustomerService, что значительно замедляет работу.</li><li>Блокировки времени выполнения: Длительная транзакция в CustomerService, которая удерживает блокировку на таблице ORDERS, может полностью заблокировать работу OrderService.</li></ul><p>Такой подход лишает микросервисы их ключевых преимуществ — независимости развертывания и масштабирования.</p><p>Правила работы с данными:</p><ul><li>У каждого сервиса должна быть собственная база данных. Сервис владеет своими данными — другие сервисы получают доступ только через его API.</li><li>Используйте шаблон Saga для управления распределёнными транзакциями вместо двухфазного коммита.</li><li>Реализуйте CQRS (Command Query Responsibility Segregation) для отделения операций записи от операций чтения, когда это необходимо.</li></ul><p>Общая база данных создаёт жесткую зависимость между сервисами. Изменение схемы данных в одном месте может сломать функциональность в другом. Команды теряют возможность независимо развивать свои сервисы — любая правка требует согласования со всеми участниками.</p><p>Такая архитектура лишает микросервисы их главного преимущества — автономности, и напоминает жизнь в студенческом общежитии. Вместо независимых компонентов получается система, где всё связано невидимыми нитями зависимостей.</p><h2>Ошибка 7: Неготовность организации</h2><p><b>В чем проблема: </b>Микросервисы требуют изменений в структуре команды и процессах. Если продолжать работать как с монолитом, ничего не получится.</p><p><b>Реальный кейс:</b> в одной из <a href="https://medium.com/@b.stoilov/a-short-horror-story-about-migrating-from-monolith-to-microservices-28d35801b188">компаний</a> команда разрабатывала микросервис, но была вынуждена тесно интегрировать его с унаследованным монолитом. Они зависели от его цикла выпуска, что свело на нет все преимущества независимой разработки. Проект в итоге закрыли.</p><p>Какие организационные изменения помогут:</p><ul><li>Перейдите к командам по сервисам — небольшим автономным группам, которые полностью владеют своими сервисами (You Build It, You Run It).</li><li>Внедрите культуру DevOps — автоматизацию сборки, тестирования и развёртывания для каждого сервиса.</li><li>Наладьте независимые циклы выпуска. Возможность развёртывать сервисы без координации с другими командами — ключевое преимущество микросервисов.</li></ul><p>Успешный переход на микросервисы требует изменения мышления всей команды. Разработчики должны стать ответственными за свои сервисы на протяжении всего жизненного цикла — от проектирования до эксплуатации в продакшене.</p><p>Что меняется:</p><ul><li>Ответственность. Разработчики отвечают не только за написание кода, но и за работоспособность сервиса в продакшн.</li><li>Коммуникация. Команды должны чётко определять контракты между сервисами и соблюдать их.</li><li>Экспертиза. Появляется потребность в специалистах по DevOps, наблюдаемости, распределенным системам.</li></ul><p>Микросервисы — это не про технологии, а про архитектуру и организацию работы. Без правильной культуры даже самая совершенная техническая реализация обречена на провал.</p><h2>Чек-лист для вашего перехода</h2><p>Прежде чем переходить на микросервисы, проверьте себя по этому списку:</p><ul><li>вы определили бизнес-проблему, которую будете решать микросервисами (а не просто хотим «быть как Netflix»);</li><li>спроектировали границы сервисов на основе бизнес-доменов — DDD, Bounded Context;</li><li>у каждого сервиса будет собственное хранилище данных;</li><li>предусмотрели кросс-каттинговые сервисы — логирование, аутентификацию, мониторинг;</li><li>готовы к организационным изменениям — автономные команды, DevOps;</li><li>выбрали правильный уровень детализации сервисов (не слишком мелкий);</li><li>продумали межсервисную коммуникацию — асинхронную, через события.</li></ul><p>Netflix, Amazon и Uber прошли этот путь. Они не избежали ошибок, но выстроили процессы для развития сложных систем. Микросервисы — это эволюция архитектуры и команды. Начинайте с малого, не бойтесь менять решения. Главный критерий успеха — скорость доставки ценности пользователям без поломок продакшена. Если этот показатель растет — вы на правильном пути.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как построить отказоустойчивую систему в облаке, снизить длительность простоев в год до 15 минут и уменьшить время отклика до 120 мс</title>
      <link>https://tproger.ru/articles/kak-postroit-otkazoustojchivuyu-sistemu-v-oblake--snizit-dlitelnost-prostoev-v-god-do-15-minut-i-umenwit-vremya-otklika-do-120-ms</link>
      <comments>https://tproger.ru/articles/kak-postroit-otkazoustojchivuyu-sistemu-v-oblake--snizit-dlitelnost-prostoev-v-god-do-15-minut-i-umenwit-vremya-otklika-do-120-ms?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Сергей Рыжков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-postroit-otkazoustojchivuyu-sistemu-v-oblake--snizit-dlitelnost-prostoev-v-god-do-15-minut-i-umenwit-vremya-otklika-do-120-ms</guid>
      <description><![CDATA[<p>Узнайте, как построить отказоустойчивую облачную инфраструктуру для IT-проекта. Вместе с экспертами Рег.облака и мебельного ритейлера «169» разбираем ключевые элементы миграции в облако — архитектурные принципы, автоматизацию процессов и мониторинг. 
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-postroit-otkazoustojchivuyu-sistemu-v-oblake--snizit-dlitelnost-prostoev-v-god-do-15-minut-i-umenwit-vremya-otklika-do-120-ms">Как построить отказоустойчивую систему в облаке, снизить длительность простоев в год до 15 минут и уменьшить время отклика до 120 мс</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Облачные технологии]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 20 Sep 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Современный бизнес, особенно отрасль e-сommerce, стремительно трансформируется, и облачные решения уже драйверы этой эволюции. Устаревшие локальные IT-системы часто не справляются с ростом данных и ожиданиями клиентов к скорости — они неэластичны, дороги и тормозят внедрение инноваций. Вместе с Сергеем Рыжковым (Рег.облако) и Русланом Лутидзе («169») разбираемся, как перенос в облако поможет не только решить технические проблемы, но и напрямую повлиять на выручку.</p><p>Когда растущий трафик интернет-магазина приводит к замедлению скорости генерации страниц и IT-архитектура с единым сервером перестает справляться, а еще учащаются DDoS-атаки, то перенос IT-проекта в облачные сервисы становится неизбежным. Именно с такой ситуацией столкнулся мебельный ритейлер «169». Изначально компания использовала для работы виртуальный хостинг и небольшие VPS-серверы, однако в связи с возникшими сложностями начала миграцию своих IT-систем в облако.</p><blockquote>Наша проблема была типичной: локальные серверы не могли адаптироваться к всплескам трафика во время распродаж. Процедура закупки и настройки нового оборудования занимала недели, что противоречило динамике онлайн-торговли. Единственным логичным решением стала полная миграция в облако.</blockquote><h2>Настройка облачной инфраструктуры для IT-архитектуры</h2><p>Чтобы справиться с проблемами производительности, компании «169» пришлось изменить подход к построению IT-инфраструктуры и внедрить четыре ключевых принципа современной облачной архитектуры.</p><p><b>Принцип разделения ответственности </b>стал основой новой системы. Вместо хаотичного размещения сервисов на единой платформе была реализована четкая специализация компонентов. Под базы данных выделили мощные серверы PostgreSQL с репликацией по схеме master-slave для отказоустойчивости и производительности. Поисковые функции перенесли на отдельный кластер Elasticsearch с русскоязычной морфологией, что значительно ускорило работу с каталогом товаров. Фоновые задачи и очереди обработки вынесли на специализированные серверы с Redis и RabbitMQ — это разгрузило основные веб-ноды и повысило стабильность системы.</p><p>Далее внедрили <b>многоуровневую систему кэширования</b>, которая уменьшила время отклика в шесть раз. На уровне приложения настроили Redis для кэширования шаблонов и результатов API-запросов, что снизило нагрузку на базы данных. Для статического контента подключили браузерное кэширование с оптимальными сроками жизни ресурсов. Геораспределенная CDN-сеть помогла с быстрой доставкой контента пользователям из разных регионов, уменьшив задержки до минимума.</p><p><b>Децентрализованное хранение данных</b> устранило проблемы с производительностью и надежностью медиаконтента. Все файлы были перенесены в S3-совместимое объектное хранилище, что позволило отделить хранение от логики приложения. Для безопасного доступа к приватным ресурсам реализовали механизм presigned URL с ограниченным временем жизни. Автоматизация управления данными через lifecycle-правила упростила процессы архивирования и удаления устаревших файлов, снизив затраты на хранение.</p><p>Также важным элементом стала <b>полная автоматизация процессов</b>. Внедрили CI/CD на базе GitLab с автоматическим тестированием каждого коммита — это в разы ускорило вывод новых функций на прод. Система мониторинга на Prometheus и Grafana с продуманными алертами позволила оперативно реагировать на инциденты до их влияния на пользователей. Сейчас автоматическое масштабирование ресурсов по метрикам нагрузки позволяет вести бесперебойную работу даже в периоды пикового трафика, экономя ресурсы в спокойные периоды.</p><blockquote>Мы предоставили ритейлеру отказоустойчивую и масштабируемую облачную инфраструктуру, которая позволила не только мгновенно реагировать на всплески трафика, но и значительно повысить безопасность и производительность ключевых сервисов. Облачная платформа Рег.облако стала надежным фундаментом для роста бизнеса.</blockquote><p>В таблице представлены технические характеристики основных нод, развернутых в инфраструктуре Рег.облака.</p><figure><img src="https://media.tproger.ru/user-uploads/117655/2025-09-09/ce695ca0-0c3d-4887-b9f2-81e185421f04.png" alt="" /></figure><h2>Технические результаты</h2><p>Распределенная IT-архитектура с балансировкой нагрузки и резервированием критических компонентов помогла компании добиться выдающихся показателей отказоустойчивости. Среднее время простоя сервисов сократилось до менее 15 минут в год — это соответствует уровню доступности 99,98%. Существенно улучшилась и производительность: время отклика IT-системы уменьшилось в 6 раз — с 800 мс до 120 мс, а полная загрузка страниц стала занимать 200–250 мс. Эти улучшения не только повысили удовлетворенность пользователей, но и положительно сказались на SEO-показателях проектов.</p><p>Инфраструктура продемонстрировала высокую эластичность — в период пиковых нагрузок, например при распродажах, развертывание пяти дополнительных инстансов занимает менее 10 минут без прерывания работоспособности сервисов. Кроме того, технические изменения повлияли на бизнес-метрики и помогли увеличить конверсию и оборот, а также сократить операционные расходы.</p><blockquote>Исходный план был реализован полностью, но в процессе мы усилили архитектуру несколькими важными корректировками: перешли на выделенные серверы для кластера БД из-за ограничений по приватным сетям, внедрили ELK-стек для централизованного логирования, развернули трехнодовый кластер Redis для кэширования, глубоко интегрировали CI/CD с Terraform и Ansible, подключили облачное S3-хранилище для медиаконтента и усилили защиту Anycast-DNS. Эти изменения позволили добиться максимальной отказоустойчивости и снизить нагрузку на бэкенд даже в пиковые периоды.</blockquote><p>Создание отказоустойчивой облачной инфраструктуры — сложная задача, где от технических решений напрямую зависят бизнес-результаты. Как показывает практика, инвестиции в эластичную, автоматизированную и безопасную платформу окупаются не только за счет снижения эксплуатационных расходов, но и благодаря повышенной надежности, которая защищает репутацию компании и увеличивает доходы.</p>]]></content:encoded>
    </item>
    <item>
      <title>Мое видение универсального языка программирования</title>
      <link>https://tproger.ru/articles/moe-videnie-universalnogo-yazyka-programmirovaniya</link>
      <comments>https://tproger.ru/articles/moe-videnie-universalnogo-yazyka-programmirovaniya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Миша Разаков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/moe-videnie-universalnogo-yazyka-programmirovaniya</guid>
      <description><![CDATA[<p>Рассмотрим теоретическое создание универсального языка программирования. Плавно перейдем к идее языка-конструктора. Вас ждет погружение в модульную архитетуру, лямбда-исчисления, байткод, расширяемое программирование

</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/moe-videnie-universalnogo-yazyka-programmirovaniya">Мое видение универсального языка программирования</a>»</p>]]></description>
      <category><![CDATA[Языки программирования]]></category>
      <category><![CDATA[C++]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Haskell]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 30 Aug 2025 12:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Предисловие</h2><p>В этой статье будет много моих выводов, гипотез и идей. Это моя первая статья, поэтому буду рад услышать аргументированную критику. В статье часто используется псевдокод, чей синтаксис напоминает смесь C#, C++ и Python.</p><h2>Немного о терминологии</h2><p>Много времени я программирую на C#, и в этом языке существуют некоторые термины, которые могут ввести в заблуждение программистов на C++:</p><ul><li>Под словом коллекция я подразумеваю любой набор элементов: список, массив, стек, куча, итератор, множество и т.п.</li><li>Говоря список, я подразумеваю динамический массив</li></ul><h2>Язык программирования – инструмент</h2><p>Языки можно поделить на две большие группы: предметно-ориентированные языки (aka DSL) и языки общего назначения (ЯОП). DSLи точно можно назвать инструментами, так как они по определению решают конкретную задачу. С языками общего назначения все несколько сложнее. ЯОПы имеют техническую возможность реализовать любой алгоритм, то есть полны по Тьюрингу. Но это не значит, что для каждой задачи будет эффективно использовать язык X, ведь язык Y может подходить для решения каких-то задач лучше.</p><p>Под словом “эффективно” я подразумеваю, что программистам будет морально приятно писать код на языке для решения конкретной задачи, а также, что код будет написан за минимальное количество времени.</p><p>Рассмотрим банальный пример. C# лучше использовать для написания бекенда сайтов, чем Си. Используя C#, вы делаете вклад в возможность поддержки проекта, уменьшаете порог входа в проект, пишете платформо-независимый код. Си лучше использовать в задачах, где важна производительность или низкоуровневый доступ к ресурсам. Например при написании runtime библиотеки языка, игрового движка или кода под микроконтроллеры с небольшими ресурсами.</p><p>Также некоторые языки имеют хорошую поддержку сообщества в каких-то специфических областях. Например C# и C++ – доминируют в создании игр, Python и R – преобладают в data science, JavaScript и PHP – в создании сайтов и т.п. В силу своих реализаций не все ЯОП могут решать задачи друг друга эффективно. Интерпретируемый язык никак не может быть также быстр в исполнении, как статически компилируемый, но может выигрывать в скорости кодирования.</p><p>В итоге приходим к выводу, что язык программирования, даже общего назначения, – это просто инструмент, который умеет выполнять задачи эффективно только из своей области.</p><h2>А в чем проблема?</h2><p>Проблемы начинаются тогда, когда мы обнаружим, что используемый язык программирования по какой-то причине не умеет решать специфическую задачу своей области эффективно. Например C# не всегда бывает лаконичен: иногда не хватает функциональной парадигмы или нет возможностей сократить лишний синтаксис при композиции. В таких случаях придется смириться и реализовывать функционал через костыли, например, создавая лишние интерфейсы, нужные чтобы обойти запреты языка.</p><p>Python не станет быстрым, C++ не будет хорошо подходить для написания скриптов, а Fortran не получит современный синтаксический сахар.</p><p>Если вы программировали на C# и Unity, возможно, вы писали какие-нибудь математические функции или методы-расширения. Я с разочарованием для себя обнаружил, что Unity использует C# 9-той версии, в которой нет обобщенной арифметики (ее введут в C# 11). Скорее всего, красиво и лаконично реализовать какой-нибудь метод SquareMagnitude не получится: придется писать одну и ту же реализацию для каждого типа по отдельности (дженерики не помогут: нет обобщающего интерфейса).</p><p>Помимо этих реализаций, было бы неплохо иметь реализацию с типами: byte, sbyte, ushort, uint, ulong, decimal. Хорошо, что метод SquareMagnitude достаточно маленький, но если бы это был большой метод, код стал бы еще хуже. Принцип DRY (don’t repeat yourself) плачет в сторонке.</p><h2>Extensible Programming</h2><p>Решением видится расширяемое программирование (<a href="https://en.wikipedia.org/wiki/Extensible\\_programming">Extensible Programming</a>) – это парадигма программирования, когда расширяемость возводится в абсолют. Возможности языка не фиксированы, а могут быть расширены. Программист пишет не только расширяемые модульные программы, но и программирует на расширяемом языке. Программист имеет возможность изменять синтаксис языка, исполняемый движок (компилятор/интерпретатор/JIT’а и т.п.), среду исполнения (environment) – все то, что мы называем Toolchain’ом языка. Эта парадигма позволяет кастомизировать язык программирования, добавлять eDSL (embedded DSL, встроенные в язык. Как пример: LINQ в C#). Зачастую кастомизация даже представляется как одно из основных средств программирования. Например, на расширяемом языке есть возможность добавлять новые синтаксические конструкции, изменять AST, байткод, контролировать процесс компиляции, изменять среду исполнения и т.п. Самые известные из расширяемых языков: Ruby, Lisp, Lua.</p><p>Предлагаю посмотреть <a href="https://habr.com/ru/articles/571182/">эту статью</a> на хабре, в которой демонстрируется пример написания простенького генератора кода для элегантных лямбд на Lua. Это пример расширяемого синтаксиса языка. Ruby тоже имеет расширяемый синтаксис, например для него написана библиотека <a href="https://rubygems.org/gems/superators19">superators19</a>, позволяющая <a href="https://stackoverflow.com/questions/11874579/define-custom-ruby-operator">добавлять новые операторы</a> в язык.</p><p>Конечно, расширяемое программирование нужно не всем, но эта парадигма – мощный инструмент для сложных систем, которым важны какие-то аспекты языка, которые не идут из коробки. Не всегда в языке не хватает каких-нибудь мелочей по типу лаконичности синтаксиса, но и таких важных механизмов, как верифицируемости корректности исполнения, формального распараллеливания и т.п. Используя расширения, можно добавлять такие механизмы, которые не были реализованы ранее или улучшать уже имеющиеся имплементации. Подобные фичи – достаточно сложные и реализовать их на уровне, например, препроцессора, невозможно. Приходится использовать более глубокое внедрение в используемый язык.</p><h2>Моя модель идеального языка программирования</h2><p>Идея заключается в возможности построить микро-язык на модульной архитектуре, который будет содержать в себе ядро и несколько дополнительных модулей для его комфортного расширения. Сам микро-язык будет не очень полезен, но на его основе можно будет сделать диалекты, которые уже можно будет считать полноценными языками общего назначения. При надобности, программист сможет отключать, модифицировать или добавлять модули в язык. Так язык превратится в набор модулей, буквально конструктор. Чтобы внести изменения в язык, будет достаточно понимать его работу в целом, а также понимать работу только тех модулей, с которыми предстоит работать.</p><p>Например, представим что JavaScript сделан по данной идее, и хочется добавить в язык рантайм-проверку типов (на минуту забудем про существование TypeScript). Можно было бы написать модуль, который добавляет немного нового синтаксиса в объявление переменных, обрабатывает AST (т.е. использует модуль парсера) и добавляет в каждое использование переменной/поля/т.п. проверку на ее тип.</p><p>Псевдо-код реализации модуля мог бы выглядеть как-то так:</p><h2>Абстрактные детали реализации</h2><p>Итак, какие задачи языка мы имеем на данный момент? ЯП должен:</p><ul><li>иметь возможность расширять свои frontend, middle-end, backend</li><li>иметь простой API для расширения</li><li>поддерживать большинство парадигм (функциональное, декларативное программирование)</li></ul><p>Сначала разберемся с первыми двумя задачами. Как уже было сказано ранее, архитектура языка будет модульной. Значит нужно просто для каждого модуля предусмотреть возможность интеграции с другими модулями. При этом желательно предоставить интуитивный API для доступа к различным частям модуля. Кроме того, каждому модулю не плохо иметь свое краткое описание работы, ведь для написания своих модулей придется иметь понимание всех модулей, которые должны использоваться.</p><p>Рассмотрим пример с созданием простейшего фронтенда. Простой фронтенд может быть разделен на небольших модуля: лексер и парсер. Допустим, программист захотел добавить препроцессор перед лексером. Какой удобный API можно предоставить? По моему мнению, хорошо подходят события. Пусть лексер и парсер имеют события OnPreprocess и OnPostprocess. Таким образом, например, программист будет иметь возможность обработки последовательности лексем уже после ее генерации. Также программист сможет изменять AST уже после его генерации.</p><p>Но как добавлять новые правила построения дерева или лексемизации текста? Можно создать методы по типу AddLexerRule, AddParserRule, которые будут принимать в себя экземпляры классов, имплементирующих интерфейсы ILexerNodeBuilder и IParserNodeBuilder. Но стоит учесть, что в большинстве языков грамматика не самая простая, а значит, что парсер желательно делать по мощности LR(k), LALR(k) или ALL(*). Возможно стоит сделать небольшой DSL для этих модулей, так как это сильно упростит поддержку языка в дальнейшем (или использовать уже готовый, такой как ANTLR). DSL поможет программистам не погружаться в технические подробности реализации и достаточно просто добавлять изменения в грамматику.</p><p>Но как же коллизии имен? Конечно, они будут возникать гораздо чаще, поэтому стоит предусмотреть некое подобие namespace’ов. Например, если в начале файла написано use parser.math, то парсер активирует дополнение к текущей грамматике, добавляющее математические операторы.</p><h2>Модули</h2><p>Что, если язык окажется сложным? Например, парсер будет очень тяжелым, язык будет использовать супероптимизаторы или просто модуль окажется проприетарным. В таком случае желательно иметь возможность разбросать модули по разным физическим местам. Например, чтобы разместить супероптимизатор в облаке, парсер поставить на компьютер с бОльшим количеством оперативной памяти, а доступ к проприетарному модулю давать только через web-request’ы (чтобы нельзя было получить исходный код).</p><p>Хорошим решением оказывается представлять модули как web-сервисы. Это делает модули гораздо более гибкими. Плюсы модулей как веб-сервисов:</p><ul><li>можно расположить в разных физических местах</li><li>IDE будет способна контролировать все процессы языка – особенно удобно, если нужно глубокое понимание языка, например просмотр промежуточных представлений</li><li>при возникновении потребности можно легко использовать утиную типизацию над модулями</li><li>можно использовать вообще любые языки программирования и технологии</li><li>модули гораздо проще воспринимать как логически разделенные части – повышается гибкость системы и уменьшается ее связность</li></ul><p>Буквально получается язык программирования на микросервисах. Возможно, сначала покажется, что такой подход сильно замедлит процесс компиляции, но, скорее всего, это скажется достаточно незначительно. На моем слабеньком ноутбуке запросы к локальным веб-серверам почти никогда не превышали 10мс, а значит чтобы добиться задержки хотя бы в половину секунды, придется сделать 50 запросов. Стоит учитывать, что общение между модулями происходит не очень часто, поэтому, думаю, беспокоиться о сильном замедлении не стоит.</p><p>Стоит заметить, что я предлагаю использовать модули как веб-сервисы исключительно на этапе компиляции. Конечно, модули, которые используются в рантайме должны быть сделаны эффективно, чтобы не создавать серьезного замедления исполнения.</p><h2>Парадигмы</h2><p>Язык программирования должен поддерживать большинство существующих парадигм. Но основными являются императивное и функциональное программирование. С возможностью написания модулей, из них строятся все остальные парадигмы.</p><p>Рассмотрим поддержку функционального программирования. Как известно, сердце функциональной парадигмы – это лямбда-исчисления. Думаю, что как ядро языка – отлично подойдут чистые нетипизированные лямбда-исчисления. Уже с помощью модулей можно дополнять λ-исчисления до system F, λ-исчислений с зависимыми типами или других исчислений. То есть достраивать ядро до нужной мощности и уже на имеющейся основе строить язык. Также фишка такого подхода в том, что, используя синтаксический сахар и монады, в язык можно добавить лаконичное императивное программирование. Но стоит быть аккуратным с чистыми функциями. Нужно учесть, что функция не будет чистой, если внутри себя она использует платформозависимые функции или вызывает внешние нечистые функции.</p><p>Но язык, ядром которого будут лямбда-исчисления, скорее всего, не будет иметь возможности быть оптимально скомпилирован под популярные архитектуры (x86-64, ARM, RISC-V). Для языков, которым важна производительность, размер программы, потребление памяти и т.п., предлагаю ввести иное ядро. Это ядро должно быть универсальным для любого императивного языка программирования, а также должно иметь возможность глубоких оптимизаций.</p><p>У ядра на лямбда-исчислениях есть еще некоторые проблемы: я не придумал, как устранить зависимости между модулями в такой реализации, поэтому, возможно, будет хорошо эмулировать работу исчислений на байткоде императивного языка. Будет написан еще один модуль, превращающий промежуточное представление лямбда-исчислений в байткод (не скупимся на количество IR’ов ради гибкости и удобства).</p><h2>Байткод императивного языка</h2><p>Итак, ядро императивного языка программирования, конечно, будет представлять собой байткод. Сначала я думал использовать какую-нибудь видоизмененную машину Тьюринга, но решил, что она слишком сильно оторвана от текущих процессоров и нет смысла даже пытаться адаптировать ее под современные системы. Самая сложная задача проектирования байткода – он должен подходить под любой императивный язык, но при этом иметь возможность оптимизировать выходной код настолько, насколько это возможно. Сразу же хочется добавить поддержку LLVM для статической компиляции, а также компиляцию в dotnet bytecode, java bytecode для поддержки JIT компиляции. Если тщательно формализовать байткод, то даже есть надежда написать супероптимизатор на middle-end уровне.</p><p>Сделать один универсальный байткод, конечно, невозможно. Снова прибегнем к модульной архитектуре. Пусть инструкция байткода представляет собой такой тип:</p><p>То есть кортеж из типа байткодовой инструкции и трех списков. Изначально байткод будет иметь только один тип инструкции: call. Массивы содержат только <a href="https://ru.wikipedia.org/wiki/%D0%94%D0%B5%D0%BB%D0%B5%D0%B3%D0%B0%D1%82_(%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5)">делегаты</a> на функции (указатель + сигнатура + доп. информация). Байткод буквально представляет собой <a href="https://ru.wikipedia.org/wiki/%D0%A8%D0%B8%D1%82%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4">шитый код</a> (threaded code) – массив вызовов функций (но не обязательно чистых). Массивы различаются лишь порядком применения операций. В массив [0] добавляются делегаты, которые должны быть исполнены перед делегатами массивов [1] и [2]. Грубо говоря получаем делегаты, которые обязательно должны быть исполнены перед или после исполнения других делегатов. Зачем это нужно, рассмотрим позже. Стоит отметить, что вместо таких массивов подошел бы также multiset&lt;(float, delegate)&gt; делегатов (отсортированный список по приоритету вызовов).</p><p>Существует несколько видов байткода: стековый, регистровый и в SSA форме. Для упрощения будем использовать стековый байткод, так как он легко переводится в регистровую и SSA форму. Да и сам по себе, гораздо более абстрактный. Регистровый байткод имеет привязку к количеству регистров, а байткод должен не зависеть от архитектурных особенностей. SSA форма – удобный вариант, но он более сложный. При разработке модулей он может сильно помешать, так как придется “жанглировать” передачами регистров, строить CFG.</p><h2>Продолжение байткода</h2><p>Представим ситуацию. Мы уже имеем минималистичный язык программирования: уже существуют локальные переменные, метки, вызовы внешних функций и другой примитивный функционал. Сейчас типизация динамическая, но хочется внедрить рантайм проверки типов, как в примере с JavaScript. Реализация примерно такая же: добавляем новое правило в лексер, обходим узлы AST с использованием переменной, внедряем assert как ноду перед использованием переменной. Но вдруг программист обнаруживает, что ему не хватает структур в языке. Обычных базовых структур, которые просто хранят значения, в которых нет даже методов (их можно добавить, но без методов проще). Итак, программист написал модуль с поддержкой подобного синтаксиса:</p><p>который превращается в подобный байткод:</p><p>Главная проблема в том, что ни на этапе AST, ни на этапе байткода инструкции LoadField и SetField не получается распознать, как использование переменной. Конечно, на уровне AST невозможно распознать такую конструкцию, так как создаются новые типы нод. А вот с байткодом есть возможность подружиться, если продумать типы инструкций. Можно сказать, что тип инструкции байткода – это ее тег. Первая мысль – делать строгие инструкции, например SetLocal, LoadGlobal, LoadMethodReference и другие. Но проблема таких типов в том, что они слишком конкретны.</p><p>Постараемся развить абстракции. Оставим только инструкции Set и Get, которые будут оперировать интерфейсами (точнее классами/структурами, имплементирующими их) ISettable и IGettable. Чтобы вызвать эти методы, нужны экземпляры классов. Для этого перед каждой установкой или чтением значения будем класть на стек ссылку-обертку на значение. Ссылка, в данном контексте, это не всегда указатель на ячейку памяти. Это абстрактный указатель на само значение. Класс, имплементирующий ISettable и IGettable. Ссылка может быть регистром, реальным указателем или быть ячейкой словаря (map’ы). Рассмотрим пример:</p><p>Как видно, логика написана достаточно абстрактно, нет никакой конкретики. Как работает ISettable – неясно, да и не нужно. Наша задача – написать относительно простой, максимально обобщенный код.</p><p>Инструкции байткода также могут возвращать значения. Первая мысль реализации: как и в обычных функциях, возвращаем значение через return. Но в байткоде встречаются такие инструкции, как swap. Они принимают два значения и возвращают два значения. Поэтому, для удобства, стоит возвращать значения через аргументы-ссылки, предназначенные только для записи (в C# – ключевое слово out). Например:</p><p>Возвращаясь к проблеме типизации и структур, теперь модуль типизации может вставлять проверки перед каждой инструкций типа Set/Get, а модуль структур будет генерировать код установки и получения значений именно через Set и Get. Тут нам и пригодятся три списка делегатов в инструкции. Проверка типов обязана исполнятся перед тем, как основная инструкция будет выполнена, поэтому проверку стоит поместить в нулевой список делегатов. Также плюсом будет то, что модули работают на уровне байткода, а не AST. Это позволит без боли менять фронтенд языка и не переписывать большинство модулей.</p><p>Теперь, понимая смысл трех списков, можем дать им названия: pre-ops, main-ops, post-ops – делегаты, исполняемые, соответственно, до, во время и после совершения основной операции.</p><p>В результате реализации байткода, придерживаясь таких идей, удастся убрать зависимость между модулями. Модуль типизации может ничего не знать о модуле структур. Аналогично модуль сборщика мусора ничего не будет знать о модуле синтаксического сахара.</p><h2>Перфоманс байткода</h2><p>Скорее всего, сложилось ощущение, что при компиляции такого байткода получится очень неэффективный ассемблерный код. В принципе, так и есть. При компиляции без некоторых оптимизаций итоговый код не уберет абстракции, не уберет виртуальные вызовы методов и в принципе не заметит некоторые паттерны, которые можно было бы упростить. Поэтому, если временем компиляции можно немного пожертвовать в пользу перфоманса, нужно в первую очередь (то есть перед всеми остальными оптимизациями) инлайнить все методы шитого кода. Уже после инлайнинга этих методов производить оптимизации. Снова рассмотрим пример:</p><p>Как видно, при достаточно глубоком инлайнинге (2-3 шага) можно сильно упростить код. Уже после инлайнинга, оптимизатор паттернов может сильно упростить некоторые под-коды. Например можно научить оптимизатор распознавать обращение к классу VariableWrapper</p><p>и заменять обращения на их быстрые аналоги (как было показано в примере, переменная была размещена в регистре rax, а не в словаре). Аналогично можно применить и другие оптимизации. А те инструкции, для которых оптимизатор не смог сгенерировать оптимизированный код, будут исполняться как обычно, в лоб. Если написано обращение к словарю, то просто вызываем переданную функцию. Такой подход позволяет писать как высокопроизводительные коды, благодаря оптимизаторам, так и универсальные, если оптимизаторы, например, не умеют компилировать код под какую-то платформу.</p><p>Конечно, качественная компиляция такого байткода – долгий процесс. Поэтому можно контролировать уровни оптимизации. Отключив инлайнинг, свертку констант и т.п., можно добиться достаточно глупой, но быстрой компиляции. Код просто будет представлять собой вызовы функций. И наоборот, задав глубину инлайнинга, включив свертку констант, аллоцируя структуры в регистрах или на стеке, векторизуя обработку данных, можно достичь высокой скорости исполнения.</p><p>Обсуждаемый байткод легко перевести в байткод виртуальной машины dotnet или JVM. Также не сложно перевести в LLVM IR, чтобы уже этот фреймворк применял сильные оптимизации.</p><h2>Рассматриваем узкие места</h2><h2>Дебаггинг</h2><p>Возможно отладка станет проблемой при фиксе ошибок компиляции байткода. Не плохой идеей кажется отслеживание, какие инструкции байткода были сгенерированы какими модулями, чтобы потом искать ошибку именно в них. Реализовать это можно достаточно просто через инкапсуляцию логики в классе коллекции байткода. Аналогичное замечание относится и к AST, ассемблерному листингу и другим промежуточным представлениям. Также хорошо добавить логирование почти каждого действия при изменении промежуточных представлений. Для совершенствования процесса отладки можно написать простенький интерпретатор этого байткода.</p><h2>Зависимости между модулями</h2><p>В худшем случае, количество зависимостей будет O(n^2), где n – количество модулей. Байткод специально был спроектирован так, чтобы зависимость между модулями была минимальной. Но даже с этим условием все еще стоит стараться делать модули абстрактными и подумать заранее о написании других модулях, которые смогут расширять его исходные возможности. Когда будет написан модуль с типизацией, нужно подумать, как улучшить имеющиеся абстракции для потенциальных будущих модулей с ее улучшением (вспомним <a href="https://ru.wikipedia.org/wiki/%D0%9B%D1%8F%D0%BC%D0%B1%D0%B4%D0%B0-%D0%BA%D1%83%D0%B1">лямбда-куб</a>).</p><h2>Общее API модулей</h2><p>Также мне нравится идея с веб-сервисами тем, что модулям придется общаться через json’ы. Это сразу заставит продумать API создаваемого модуля. Мучаться с бинарной совместимостью не придется, так как обмен информацией достаточно высокоуровневый и многие проблемы решают такие протоколы как, например, http.</p><h2>Заключение</h2><p>Не думаю, что когда-нибудь будет создан один универсальный язык программирования, который подойдет для решения всех задач. Но, скорее всего, именно удобное окружение и продуманная архитектура поможет создавать языки как конструктор для нужных задач. Нельзя допустить роста множества DSL’ей, где один язык решает одну проблему, чтобы не пришлось каждый день учить новый инструмент, но база модулей помогла бы иметь гораздо более полезный эффект. Не хватает высокоуровневого программирования в системном языке? Подключите модуль с zero-cost абстракциями. Не хватает функционального программирования в ООП языке? Подключите модуль с синтаксическим сахаром, оператором |&gt;, ленивыми вычислениями и <a href="https://en.wikipedia.org/wiki/Uniform_function_call_syntax">unified calls</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Максимально простое объяснение устройства микрофронтендов</title>
      <link>https://tproger.ru/articles/maksimalno-prostoe-obyasnenie-ustrojstva-mikrofrontendov</link>
      <comments>https://tproger.ru/articles/maksimalno-prostoe-obyasnenie-ustrojstva-mikrofrontendov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/maksimalno-prostoe-obyasnenie-ustrojstva-mikrofrontendov</guid>
      <description><![CDATA[<p>Что такое микрофронтенды простыми словами. Объясняем на примерах, что это такое и как используется.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/maksimalno-prostoe-obyasnenie-ustrojstva-mikrofrontendov">Максимально простое объяснение устройства микрофронтендов</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 27 Aug 2025 10:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p><b>Микрофронтенды</b> – это подход к разработке пользовательских интерфейсов, при котором фронтенд большого веб-приложения разбивается на набор небольших независимых приложений (модулей). По сути, это аналог концепции микросервисов,. Вместо одного монолитного клиентского приложения, весь интерфейс составляется из нескольких отдельных частей, каждая из которых автономно разрабатывается и деплоится.</p><p>Для конечного пользователя сайт выглядит как единое целое, хотя на деле это несколько сшитых друг с другом сайтов, интегрированных друг с другом. Каждый такой фрагмент отвечает за свою область функциональности (например, поиск, личный кабинет, страница товаров ) и может развиваться независимо от других. Эти мини приложения могут быть созданы на разных технологиях и фреймворках, общаться через чётко определённые интерфейсы. Вместе они образуют клиентское приложение для работы. Так как отдельные части фронта независимы друг от друга, то сбой или изменение в одном модуле не выводит из строя весь сайт.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-08-25/a9bb5228-5882-4ca1-95b8-00bee52cbd5b.png" alt="" /></figure><p>Пример Spotify, каждый раздел — это отдельные автономные приложения, склеенные в одном клиентском интерфейсе.</p><p>Рассмотрим такой подход с позиции менеджмента и разработки.</p><h2>Разработка веб-приложения</h2><p>Разберем особенности разработки веб-приложения с микрофронтендным подходом, узнаем, стоит ли продвигать его в своей работе.</p><h3>Проект придётся сшивать из отдельных частей</h3><p>Каждый микрофронтенд — это автономный проект с отдельным репозиторием и командой, что даёт гибкость в разработке и упрощает понимание кодовой базы той части, над которой предлагается работать. Но такой подход вынуждает постоянно согласовывать интеграции с другими командами, а ещё не факт, что в компании будет человек, который знает, как функционируют отдельные части. Если такого человека нет или он был и пропал, то разбираться придётся самостоятельно, а вникать в чужой код всегда долго и больно. Помочь избежать bus factor может хорошо составленная документация, а также соблюдение стандартов написания кода, единые для всех команд.</p><blockquote>Важно, что один из узлов должен отвечать за сбор как раз всех микрофронтендов и отображении их пользователю.</blockquote><h2>В проекте можно будет экспериментировать</h2><p>В микрофронтендах каждая команда может выбирать <a href="https://micro-frontends.org/">свой стек</a> под отдельные задачи — React, Vue, Angular или всё, что душе угодно . Это позволяет экспериментировать и постепенно внедрять новые технологии, не переписывая монолит. Но такая свобода — палка о двух концах: без строгих соглашений дизайн и UX могут стать разрозненными, а при запуске страницы — нагрузка дублирующихся библиотек будет бить по производительности.</p><p>Но всё-таки это даёт возможность разрабатывать и внедрять новые технологии и практики, независимо от легаси кода. Например, если приелось делать фронтенд на React, то отдельный компонент можно реализовать на Vue. Или, если в будущем существующие библиотеки будут неконкурентоспособны, не надо будет переделывать весь проект, а только проблемные компоненты.</p><h3>Проект будет сложнее деплоить</h3><p>Миркофронтендный подход позволяет разделить процесс разработки отдельных компонентов сайта, как и контроль версий каждого из них, для каждого большого компонента свой репозиторий и свой пайплайн деплоя. Например, если в проекте перестал работать поиск из-за бага на проде, откатывать надо будет только сервис поиска, а не весь проект.</p><p>Однако настройка CI/CD для множества микрофронтендов — это сотни конвейеров, репозиториев и интеграций, которые нужно синхронизировать и контролировать, что особенно тяжело для DevOps-инженеров, которым вместо того, чтобы один раз настроить пайплайн развёртывания для фронта, придётся делать это для каждой его части. К тому же добавляется ещё один этап развёртывания проекта - сбор фронтенда, под который нужно будет прописывать свои правила сборки.</p><h2>Как применять в проекте</h2><p>Для разработки сервисов на базе микрофронтендов есть несколько инструментов, основная задача которых отдельно рендерить компоненты и собирать их в единое приложение.</p><blockquote>Важно заранее предусмотреть падение одного из микрофронтендов и то как это обрабатывать, чтобы для пользователя это всё было единым и всегда стабильным сайтом.</blockquote><h3>Webpack Module Federation</h3><p>Module Federation — механизм, встроенный в Webpack 5, который позволяет одному фронтенду (host) динамически загружать модули из другого фронтенда (remote) во время выполнения. Это мощный инструмент для интеграции микрофронтендов: он поддерживает разделение кода, кэширование, совместное использование зависимостей (например, React или Vue) и независимую доставку каждого модуля. Module Federation работает без необходимости использовать iframe или перекомпиляции.</p><p><a href="https://webpack.js.org/concepts/module-federation/">Инструкция по использованию</a></p><h2>Менеджмент</h2><p>Рассмотрим особенности управления разработкой веб-приложения с микрофронтендным подходом, чтобы понять, когда имеет смысл использовать его подход для своих идей.</p><h2>Один проект превращается во множество</h2><p>Каждая часть интерфейса создаётся и поддерживается своей командой, нередко на разных технологиях, с собственным пайплайном, своим графиком релизов и приоритетами. С одной стороны, это ускоряет разработку, позволяет нанимать команды под конкретные зоны ответственности и гибко распределять ресурсы. С другой — мы получаем систему, собранную из разрозненных команд, где с ростом количества составляющих системы, придётся всё больше ресурсов тратить организацию разработки.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-08-25/a804f4ab-b214-43a2-a07a-c97eb11ed4f4.png" alt="" /></figure><p>Конечное единое веб-приложение представляет собой склейку результатов работы нескольких независимо рендерящихся компонентов, каждый из которых может иметь свой бэк и по факту быть полностью независимым.</p><h3>Команды будут независимы  друг от друга</h3><p>Микрофронтенды позволяют разделить команды, которые параллельно разрабатывают свои компоненты отвечающие за работу ключевых функции. Такой подход повышает мотивацию, людям проще видеть результаты своего труда: они меньше от него отчуждены разбиение на маленькие команды позволяет бороться с <a href="https://ru.wikipedia.org/wiki/%D0%AD%D1%84%D1%84%D0%B5%D0%BA%D1%82_%D0%A0%D0%B8%D0%BD%D0%B3%D0%B5%D0%BB%D1%8C%D0%BC%D0%B0%D0%BD%D0%B0">эффектом Рингельмана</a> по мере роста команды. Но автономия  — это и изоляция: одна команда может принять техническое решение, которое затрудняет интеграцию с другими, а синхронизация между командами требует постоянных митов, созвонов или сотен сообщений.</p><p>Управлять несколькими созависимыми подразделениями сложнее, чем вести один проект с централизованным контролем.К тому же, если у одной из команд ушёл разраб, заменить его разрабом из другой не получится, технологии разные и навыки тоже.</p><h3>Команды получают поле для удачных и не очень экспериментов</h3><p>Команды могут внедрять новые технологии, не дожидаясь переписывания всего приложения: один микрофронтенд можно сделать на React, другой — на Vue или Svelte. Это даёт гибкость в найме, миграциях и экспериментах. Но чем больше технологий используется, тем сложнее следить за их использованием и труднее помнить их особенности.</p><p>В какой-то момент менеджер вместо одной технологической карты управляет целой экосистемой со своими внутренними правилами.</p><h3>Появляются FrontOps</h3><p>Микрофронтенды требуют дополнительных усилий по настройке и деплою, для этого понадобится расширять команду DevOps. Эта команда может расширяться настолько, что внутри неё придется выделить подгруппу DevOps, которая будут заниматься исключительно деплоем фронтов — называться они будут FrontOps.</p><h2>Вывод</h2><p>Микрофронтенды — это в первую очередь инструмент для масштабирования разработки. Если приложение действительно большое, команды автономны, а новые фичи выходят каждую неделю, разделение фронтенда на независимые модули ускорит релизы, снизит риски и позволит гибко внедрять технологии.</p><p>Однако мутант из разнородных сервисов быстро становится неуправляемым, если нет четкой и структурированной организации внутри его частей. Выбирайте микрофронтенды, когда бизнес‑скорость важнее архитектурной простоты, и у вас есть настроенная DevOps-инфраструктура. В остальных случаях монолит с хорошей модуляризацией и монорепозиторием может оказаться дешевле и надёжнее.</p><p>Синьор-фронтендер из ВК Маргарита Лукина, автор телеграм-канала <a href="https://t.me/frontend_kitchen">«Фронтенд кухня»</a> отмечает, что микрофронты — не всегда удачное решение:</p><p>«Микрофронтенды — хорошее решение, если команды действительно автономны. Я видела примеры, когда проект разделяли на микрофронтенды, но при этом одно приложение зависело от другого: чтобы добавить фичу, необходимо было делать изменения сначала в одном месте, а затем в другом. Это — верный знак, что что-то пошло не так: в этом случае процесс разработки усложнится, а сроки затянутся. Прежде чем переходить на микрофронтенды, попробуйте логически разделить ваш проект по директориям и убедиться, что для изменения одной фичи в проекте приходится редактировать код только в одной директории. Следующим шагом можно переходить на микрофронтенды».</p><p>Делитесь своим опытом работы с микрофронтендами в комментариях и не забывайте сохранять наши статьи!</p>]]></content:encoded>
    </item>
    <item>
      <title>Квест: какой ты магистр информационных технологий</title>
      <link>https://tproger.ru/quiz/kvest--kakoj-ty-magistr-informacionnyh-tehnologij</link>
      <comments>https://tproger.ru/quiz/kvest--kakoj-ty-magistr-informacionnyh-tehnologij?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/quiz/kvest--kakoj-ty-magistr-informacionnyh-tehnologij</guid>
      <description><![CDATA[<p>Выбираем магистратуру в 2025 году вместе с YADRO. Пройди квест и узнай, какой ты профессионал в мире информационных технологий. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/quiz/kvest--kakoj-ty-magistr-informacionnyh-tehnologij">Квест: какой ты магистр информационных технологий</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Викторины]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 13 Aug 2025 15:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Привет! Ты кибер-скиталец, который оказался в городе YADROpolis. Пройди этот мини-квест, чтобы узнать, какая миссия определит твою судьбу на ближайшие несколько лет.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработчик объяснил, почему Vibe Coding — это лишь хайповый инструмент, а не замена инженерам</title>
      <link>https://tproger.ru/news/razrabotchik-obyasnil--pochemu-vibe-coding---eto-liw-hajpovyj-instrument--a-ne-zamena-inzheneram</link>
      <comments>https://tproger.ru/news/razrabotchik-obyasnil--pochemu-vibe-coding---eto-liw-hajpovyj-instrument--a-ne-zamena-inzheneram?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/razrabotchik-obyasnil--pochemu-vibe-coding---eto-liw-hajpovyj-instrument--a-ne-zamena-inzheneram</guid>
      <description><![CDATA[<p>Vibe Coding — не замена инженерам, а инструмент для прототипов: разработчик объяснил, почему ИИ пока не вытеснит архитекторов</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/razrabotchik-obyasnil--pochemu-vibe-coding---eto-liw-hajpovyj-instrument--a-ne-zamena-inzheneram">Разработчик объяснил, почему Vibe Coding — это лишь хайповый инструмент, а не замена инженерам</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 31 Mar 2025 17:30:18 GMT</pubDate>
      <content:encoded><![CDATA[<p>Vibe Coding — термин, недавно предложенный Андреем Карпати, со-основателем OpenAI. Он описывает это как новый способ программирования: вместо работы с кодом напрямую, разработчик просто общается с ИИ, наблюдает результат и... отдается вайбу.</p><p>Звучит модно — и действительно, соцсеть X быстро заполнилась восторженными постами о том, что «кодеры теперь не нужны». Но один из инженеров <a href="https://serce.me/posts/2025-31-03-there-is-no-vibe-engineering">решил</a> остудить пыл: он объяснил, почему Vibe Coding — это не революция, а просто удобный способ быстро набросать прототип.</p><h2>Кодить и проектировать — не одно и то же</h2><p>Автор сразу уточняет: программирование — это не инженерия. Инженер не просто пишет код, он строит системы, которые выдерживают нагрузку, защищены от уязвимостей и готовы развиваться годами.</p><p>Vibe Coding же — это максимум «написал, глянул, сойдет». Ни о каком проектировании, поддержке пользователей или долгосрочном развитии речи не идет.</p><p>ИИ может помочь создать кусок кода или быстро собрать MVP. Но это всего лишь момент времени. Настоящая инженерия работает с системой в динамике — и тут Vibe Coding пока бесполезен.</p><p>Как минимум потому, что сложно выразить технические ограничения или требования к масштабируемости в виде одного промпта.</p><h2>Настоящее «вайб-инженерство» — это обычная инженерия, но строже</h2><p>Можно представить, что в будущем появятся системы, полностью собранные из «вайб-кода». Но чтобы они работали в продакшене, их придется покрывать жестким тестированием, мониторингом, канарейками и регламентами. По сути, это та же инженерия, только с еще большим уровнем контроля.</p><p>То есть роль инженера не исчезнет, а станет сложнее: он будет больше похож на архитектора или платформенного инженера, а не просто кодера. Такой специалист не пишет каждую строку сам, но отвечает за то, чтобы все части системы не развалились.</p><p>Если хочется называть это Vibe Engineering, то пожалуйста. Но по сути — это всё та же инженерия, только без IDE.</p><h2>Будущее — не сейчас</h2><p>Когда этот подход станет реальностью? Хороший индикатор — вопрос «согласны ли вы выйти на дежурство по системе, полностью собранной ИИ?». Пока почти все отвечают «нет». И это многое говорит.</p><p>Итог? Vibe Coding — полезный инструмент. Он помогает быстро стартовать, сделать демо или сэкономить время.</p><p>Но создавать продакшн-системы только на его основе — пока утопия. Да, код станет менее важной частью профессии. Но инженеры по-прежнему будут нужны — просто их работа изменится.</p>]]></content:encoded>
    </item>
    <item>
      <title>Hexagonal Architecture: Почему старая добрая многослойка больше не работает?</title>
      <link>https://tproger.ru/articles/razbiraem-hexagonal-architecture-i-ee-primenenie</link>
      <comments>https://tproger.ru/articles/razbiraem-hexagonal-architecture-i-ee-primenenie?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/razbiraem-hexagonal-architecture-i-ee-primenenie</guid>
      <description><![CDATA[<p>Что такое Hexagonal Architecture. Показываем основные возможности применения гексагональной архитектуры в программировании. Рассматриваем пошаговую инструкцию и основные нюансы</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/razbiraem-hexagonal-architecture-i-ee-primenenie">Hexagonal Architecture: Почему старая добрая многослойка больше не работает?</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 20 Mar 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Гексагональная архитектура — подход к организации кода, направленный на создание гибких и легко поддерживаемых программных продуктов. Паттерн отделяет логику приложений от внешних интерфейсов, тем самым обеспечивая автономность и модульность компонентов системы. Другое название подхода — порто-ориентированная архитектура, то есть привязанная к портам и адаптерам.</p><p>Узнаем, что собой представляет гексагональная архитектура, каковы ее основные принципы, как она работает и применяется.</p><h2>Основные принципы Hexagonal Architecture</h2><p>Мобильные и веб-приложения становятся все более сложными и производительными, поэтому требования к ним постоянно повышаются — ПО должно быть адаптивным, тестируемым и модульным, а компоненты в нем — независимыми. Hexagonal Architecture в Java предоставляет разработчикам эффективные решения, позволяющие достигать максимальных результатов с минимальными усилиями.</p><p>Основная логика приложения в формате гексагональной архитектуры: центральное ядро, которое соединяется с внешним миром через специальные интерфейсы — порты и адаптеры.</p><p>Визуально этот архитектурный паттерн можно представить в виде шестиугольника (гексагона), на всех сторонах которого находятся различные адаптеры, соединяющие центр с внешними сервисами — отсюда название подхода. Схема отражает гибкость такой структуры — новые адаптеры можно добавлять либо удалять без ущерба для ядра.</p><p>Принцип работы Hexagonal Architecture заключается во взаимодействии портов и адаптеров. Порты — это границы приложения, взаимодействующие с внешней для ядра средой: интерфейсами, сообщениями, базами данных, запросами командной строки и т.д. Адаптеры отвечают за трансляцию данных между разными интерфейсами.</p><figure><img src="https://media.tproger.ru/user-uploads/113565/2025-03-17/16a29764-dbf7-45f4-b33f-13b8d85d6882.png" alt="" /><figcaption>Схема Hexagonal Architecture</figcaption></figure><h3>Связь Hexagonal Architecture с принципами SOLID</h3><p>Шестиугольная архитектура в java или скриптоподобном языке PHP работает по принципам объектно-ориентированного программирования SOLID. Согласно концепции, код нужно максимально упрощать, избегая запутанных конструкций.</p><p>Ключевым для Hexagonal Architecture считается пятый принцип SOLID — Dependency Inversion Principle (DIP), т.е. принцип инверсии зависимостей. Он гласит, что модули верхнего порядка не должны зависеть от модулей нижнего порядка. Код пишется таким образом, чтобы зависимости между компонентами приложения основывались на абстракциях вместо конкретных реализаций. Так, компоненты будет просто заменить, не воздействуя на другие части системы.</p><p>Сегодня компании, специализирующиеся на создании сложного и масштабируемого софта, активно используют паттерн для повышения устойчивости, удобства поддержки и развития своих продуктов.</p><h2>Структура Hexagonal Architecture</h2><p>Шестигранная программная архитектура в java предполагает, что приложение имеет одинаковую управляемость со стороны пользователей, других приложений и автоматических текстов. Для логики не имеет значения источник вызова. Свойства Hexagonal Architecture обеспечиваются ее структурой, которую стоит рассмотреть в подробностях.</p><h3>Центральное ядро — логика основного домена</h3><p>Бизнес-логика — центральная часть любого приложения, отвечающая за реализацию внутренних правил и процессов. В рамках Hexagonal Architecture ядро рассматривается как ключевой компонент всей системы, куда поступают входные данные и откуда выходят через порты.</p><p>Бизнес-логика отвечает за обработку запросов, управление данными и выполнение всех требований заказчика. Грамотная организация бизнес-логики гарантирует корректную и эффективную работу приложения независимо от изменений в пользовательских интерфейсах или внешних системах.</p><p>Гексагональная архитектура изолирует бизнес-логику от любых внешних зависимостей. За изоляцию отвечают порты, которые берут на себя все сторонние взаимодействия. Это обеспечивает приложениям гибкость и поддерживаемость — то есть высокую скорость, удобство и простоту при внесении изменений.</p><p>На практике изоляция означает, что изменения в одной части системы никак не влияют на другие компоненты приложения, что на порядок упрощает разработку, тестирование и дальнейшее развитие продукта.</p><h3>Порты</h3><p>Порты — интерфейсы, которые определяют способы взаимодействия бизнес-логики приложения с внешними системами. Они работают как контракты, задающие стандарты для коммуникации.</p><p>Используется два типа портов:</p><ul><li>Управляющие. Их используют внешние участники в процессе взаимодействия с системой — например, элементы пользовательского интерфейса. Управляющие порты определяют способы, которые внешние клиенты применяют для запросов к бизнес-логике.</li><li>Управляемые. С их помощью приложение взаимодействует с внешними сервисами — данными, системами обмена сообщениями, сторонним API. Эти порты отвечают за методы, обеспечивающие полноценную работу приложения.</li></ul><p>Важная особенность портов в рамках гексагональной архитектуры и принципа DIP — их абстрактная природа, которая сохраняет независимость логики от деталей реализации. Порты определяют форматы ввода/вывода данных и протоколов, по которым ядро взаимодействует с адаптерами.</p><h3>Адаптеры</h3><p>Задача адаптеров — реализовать работу портов и преодолеть разрыв между логикой домена и любыми внешними сервисами. Эти компоненты структуры переводят внешние данные в формат, понятный приложению, и проделывают обратную трансформацию.</p><p>Применяется два типа адаптеров:</p><ul><li>Входные. Преобразуют данные извне (например запросы HTTP или пользовательский ввод) в команды, понятные ядру приложения.</li><li>Выходные. Преобразуют выходные данные логики домена в команды для работы с внешними компонентами. Например, получив преобразованный запрос HTTP, приложение с помощью управляемого адаптера выдает результат в формате JSON.</li></ul><p>Адаптеры — ключевой компонент, обеспечивающий системе гибкость. Базовая бизнес-логика, благодаря адаптерам и портам, общается с разными интерфейсами, что особенно важно для масштабируемых приложений. С каждым портом могут взаимодействовать несколько адаптеров, что обеспечивает бизнес-логике независимость от типа входных устройств.</p><h2>Преимущества Hexagonal Architecture</h2><p>Зачем использовать гексагональную архитектуру в программировании на Java? Все дело в преимуществах, которые обеспечивает этот паттерн. Рассмотрим основные плюсы «шестигранного» подхода.</p><h3>Строгое разделение ответственности</h3><p>Бизнес-логика изолирована от внешних зависимостей, что позволяет разрабам сосредоточиться на основных задачах приложения, не заморачиваясь на специфике внешних интеграций.</p><p>За взаимодействия отвечают порты и адаптеры — разделение функций значительно упрощает не только разработку, но и все остальные этапы реализации приложения как готового продукта.</p><p>Разделение ответственности обеспечивает гибкость программного обеспечения, то есть возможность менять бизнес-логику независимо от способов взаимодействия с внешними системами. Порты и адаптеры выступают посредниками между ядром и внешним миром, что минимизирует взаимозависимости.</p><h3>Повышенная тестируемость</h3><p>Поскольку логика домена изолирована от внешних сервисов, программисты могут создавать модульные тесты для бизнес-правил без использования заглушек и имитации.</p><p>Тестируемость — одно из ключевых преимуществ Hexagonal Architecture. Она позволяет проверять работу отдельных компонентов системы в изоляции, что делает тесты более корректными и эффективными.</p><p>Снижаются затраты на обнаружение и исправление багов, прогеры быстрее выявляют и устраняют проблемы. Итог такого подхода — более надежное и устойчивое ПО.</p><h3>Улучшенная адаптивность</h3><p>Разделение ответственности исключает влияние внешних зависимостей на основную логику приложения. Продукты проще обновлять, рефакторить (перерабатывать код с целью его упрощения), менять внешние компоненты — подключать новые адаптеры при смене баз данных и добавлении новых API. При этом на основной функционал такие изменения не повлияют.</p><h3>Масштабируемость</h3><p>Благодаря модульной структуре любую часть системы можно расширить, модифицировать или заменить, не затрагивая при этом другие компоненты.</p><p>Масштабируемость делает Hexagonal Architecture идеальным выбором для крупных и постоянно развивающихся проектов типа банковских приложений, в которые регулярно добавляются новые функции.</p><h3>Упрощенная интеграция</h3><p>Наличие портов и адаптеров позволяет без проблем подключать приложения к разным типам внешних сервисов, что повышает адаптивность и конкурентоспособность продукта. Бизнес-логика не зависит от конкретных технологий или протоколов — ее можно интегрировать в новые системы без переписки кода.</p><p>Если одну базу данных нужно заменить на другую, это не вызовет у программиста никаких сложностей. Или если изначально приложение использовало REST API, а затем возникла необходимость перейти на GraphQL, достаточно создать новый адаптер для GraphQL, не трогая бизнес-логику.</p><p>Учитывая, что тестирование новых интеграций упрощено, подключение дополнительных сервисов становится предельно доступной и быстрой процедурой. На этапе проверки реальные адаптеры можно заменить на их тестовые версии, что позволяет моделировать различные сценарии без необходимости подключения к реальным системам.</p><p>Такой подход не только ускоряет тестирование, но и снижает затраты: согласно исследованиям, использование модульных архитектур снижает расходы на интеграцию на 30%.</p><h2>Недостатки и сложности применения</h2><p>Минусов у гексагональной архитектуры немного:</p><ul><li>Подход может оказаться слишком сложным для простых продуктов — затраты будут избыточными по сравнению с полученными результатами. В профессиональной среде это называется оверинжирирг.</li><li>Повышенное количество абстракций иногда становится проблемой для приложения и отражается на его функциональности.</li></ul><p>Отдельно стоит рассмотреть сложности в начальной реализации подхода. К числу наиболее распространенных ошибок при внедрении Hexagonal Architecture относится недостаточное разделение ответственности между компонентами системы. Возникает зависимость бизнес-логики от внешних интерфейсов, что противоречит базовым принципам гексагона.</p><p>Чтобы избежать ошибок, необходимо уделить максимум внимания проектированию портов и адаптеров. Важно также проводить регулярные ревью кода и применять автоматизированное тестирование, чтобы убедиться, что архитектурные принципы соблюдаются.</p><h2>Сравнение с другими архитектурными подходами</h2><p>Многослойная архитектура (Layered Architecture), которая используется еще с 1970-х годов, представляет собой классический подход к построению программных систем. Такие приложения разделены на зависимые друг от друга слои, каждый из которых отвечает за конкретный аспект функционала — ядро, интерфейс, логику.</p><p>В отличие от такого паттерна, Hexagonal Architecture вводит концепцию портов и адаптеров, которая минимизирует зависимость между слоями. Это делает продукт более устойчивым, поскольку изменения в одном компоненте минимально затрагивают остальные части.</p><p>К примеру, в классической многослойной архитектуре изменение интерфейса доступа к данным может требовать значительных изменений в бизнес-логике, тогда как в Hexagonal Architecture такое обновление ограничивается заменой адаптера.</p><p>Гексагональная архитектура имеет схожие черты с Clean Architecture. Более того, Clean Architecture основана на принципах шестигранного паттерна.</p><p>Здесь тоже есть разделение на уровни ответственности, которые в значительной степени изолированы друг от друга. При этом модули также зависят не от конкретных компонентов, а от абстракций, что позволяет без проблем адаптировать приложение к различным технологиям.</p><p>Можно сказать, что Clean Architecture — это эволюция паттернов разработки. В таких приложениях код слабо зависит от изменений в изолированных слоях (бережем труд программиста), а сам продукт легко тестировать, масштабировать и поддерживать.</p><h2>Примеры реализации</h2><p>Реальные примеры Hexagonal Architecture и изолированной бизнес-логики можно найти в банковских приложениях. В таких системах логика обработки транзакций, проверки баланса и расчета комиссий отделена от пользовательских интерфейсов и взаимодействия с базами данных. Такое разделение упрощает обновление или расширение функционала — сотрудникам ИТ-отдела не придется вмешиваться в ядро, когда потребуется прикрепить новую опцию.</p><p>Адаптеры используются и в таких крупных проектах, как Amazon и Netflix — масштабируемость позволяет развивать приложения практически в реальном времени, улучшая пользовательский интерфейс и быстродействие.</p><p>В онлайн-магазинах адаптеры с одинаковым успехом обрабатывают запросы от веб-приложений, мобильного софта и REST API, направляя их в одну и ту же бизнес-логику. Разработчики могут добавлять новые интерфейсы, например, голосовых помощников, без изменения базового кода.</p><h2>Рекомендации по внедрению Hexagonal Architecture</h2><p>Внедрение Hexagonal Architecture в проект начинается с анализа текущей архитектуры приложения и определения, какие компоненты можно выделить в качестве бизнес-логики. Это важно, поскольку основная цель архитектуры заключается в четком отделении ядра продукта от интерфейсов взаимодействия.</p><p>Следующий шаг — проектирование портов, которые будут связывать бизнес-логику и внешние адаптеры. На этом этапе определяют, какие операции и данные должны быть доступны для взаимодействия с внешними системами.</p><p>Затем разрабатываются адаптеры, которые реализуют взаимодействие через порты с конкретными внешними системами (базами данных, веб-интерфейсами или API).</p><p>Завершающий этап — тестирование новой архитектуры: нужно убедиться, что она соответствует требованиям и хорошо интегрируется в существующую экосистему приложения.</p><p>Для успешной интеграции Hexagonal Architecture в проект стоит начать с небольших изолированных модулей, чтобы минимизировать риски и протестировать подход. Важно уделить внимание документированию портов и адаптеров, чтобы все в команде могли легко понять их назначение и использовать в дальнейшей работе.</p><p>Также необходимо применять современные инструменты, которые поддерживают модульность и тестируемость кода. Например, автоматизация тестирования значительно упростит проверку соответствия новой архитектуры текущим требованиям. Наконец, регулярно проводить ревизию архитектуры, чтобы убедиться, что она все еще удовлетворяет потребности проекта по мере его роста и развития.</p>]]></content:encoded>
    </item>
    <item>
      <title>Топовые паттерны для разработки архитектуры ПО</title>
      <link>https://tproger.ru/articles/topovye-patterny-dlya-razrabotki-arhitektury-po</link>
      <comments>https://tproger.ru/articles/topovye-patterny-dlya-razrabotki-arhitektury-po?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Анна Ельцова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/topovye-patterny-dlya-razrabotki-arhitektury-po</guid>
      <description><![CDATA[<p>
Паттерны проектирования архитектуры ПО. Показываем виды паттернов и их особенности. Рассматриваем пошаговую инструкцию и основные нюансы ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/topovye-patterny-dlya-razrabotki-arhitektury-po">Топовые паттерны для разработки архитектуры ПО</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Облачные технологии]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 24 Feb 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда речь идет о проектировании системы, важно понимать, что выбор архитектурного паттерна зависит от множества факторов. Универсального решения вы не найдете, но есть проверенные подходы, которые помогают сделать выбор в пользу эффективных инструментов. При этом важно учитывать:</p><ul><li>каких вы придерживаетесь трейдофов;</li><li>какие требования у бизнеса, команды;</li><li>какое техническое окружение;</li><li>насколько система должна быть масштабируемой и отказоустойчивой;</li><li>какая модель данных наиболее эффективная.</li></ul><p>Вместе с <a href="https://solvery.io/ru/mentor/igorwalther?utm_source=article&amp;utm_medium=partner&amp;utm_term=tproger&amp;utm_content=Architecture&amp;utm_campaign=Igor_Panasyuk">Игорем Панасюком</a>, разработчиком в финтехе Яндекса и ментором <a href="https://solvery.io/?utm_source=article&amp;utm_medium=partner&amp;utm_term=tproger&amp;utm_content=cicd&amp;utm_campaign=main_page">Solvery</a>, разбираемся, какие есть антипаттерны в разработке архитектуры, а какие подходы действительно эффективны и для чего нужны.</p><h2>Для начала: антипаттерны и частые ошибки</h2><p>Антипаттерны могут привести к сложному, плохо поддерживаемому или неэффективному коду. Ниже — несколько примеров.</p><h3>Distributed Monolith</h3><blockquote>На мой взгляд, частая проблема в настоящее время — это Distributed Monolith, то есть микросервисы, которые имеют очень сильную связность — в таком случае лучше сделать обычный монолит.</blockquote><p>В результате:</p><ul><li>При деплое одного сервиса нужно обновлять все остальные.</li><li>Сервисы жёстко связаны через API.</li><li>Не получается горизонтально масштабировать.</li></ul><p>Например, у нас есть три микросервиса: заказы, оплата и пользователи. При этом сервис «Заказы» напрямую вызывает остальные сервисы, и наоборот. Так, если в одном сервисе поменяется API, остальные перестанут работать.</p><p>Чтобы исправить, можно использовать, например, асинхронную коммуникацию через Kafka.</p><h3>Микролиты</h3><p>Архитектура по идее микросервисная, но каждый микросервис настолько сложный, что превращается в «маленький монолит». Да, у сервиса есть собственная база данных, но код все равно сложно поддерживать и масштабировать.</p><p>Например, есть сервис «Каталог товаров». По идее, он должен быть простым, но в него добавили еще и данные о всех заказах, пользователях и платежах. Как результат — десятки зависимостей и сложная бизнес-логика.</p><p>Чтобы не было такой путаницы, упрощайте сервисы и следуйте принципу: «один сервис — одна ответственность».</p><h3>Многослойность</h3><p>Архитектура перегружена кучей слоев, например, API → сервис → репозиторий → DAO → DTO → и так далее. Причем каждый слой выполняет какую-то минимальную работу и банально вызывает следующий.</p><p>Каждый слой просто прокидывает вызовы и практически не несет ценности. Здесь важно помнить: если все, что делает слой, — это делегирует вызовы, он вам не нужен.</p><p>А теперь рассказываем, какие паттерны при разработке архитектуры будут эффективными.</p><h2>Паттерны для предоставления данных и управления</h2><p>Наиболее популярные паттерны для предоставления данных — MVC, MVVM и MVP. Рассказываем, чем они отличаются.</p><h3>MVC (Model-View-Controller)</h3><p>Этот паттерн делит приложение на три компонента: Model — хранит бизнес-логику, View — отображает UI, и Controller — обрабатывает ввод пользователя, следит за Model и View. Работает это так:</p><figure><img src="https://media.tproger.ru/user-uploads/101078/2025-02-21/788cc440-7188-4617-b4f9-2c11f64654a9.png" alt="" /></figure><p>Пример — пользователь хочет авторизоваться на сайте:</p><ol><li>Юзер вводит логин и пароль окно входа — через View.</li><li>View отправляет данные в Controller.</li><li>Controller передает логин и пароль в Model, которая проверяет их в базе данных.</li><li>Model возвращает результат (успешная или неуспешная аутентификация).</li><li>Controller обновляет View — он выводит либо сообщение, что все успешно, либо ошибку, например, «Неверный логин или пароль».</li></ol><h3>MVVM (Model-View-ViewModel)</h3><p>В этом паттерне тоже три компонента, но немного другие: Model — тоже отвечает за логику, View — UI, который поддерживает привязку данных (data binding), ViewModel — преобразует данные Model для View, содержит команды для обработки действий. Вот схема:</p><figure><img src="https://media.tproger.ru/user-uploads/101078/2025-02-21/b4c9d430-4fb4-4989-a7e6-8e5c4ceed127.png" alt="" /></figure><p>Пример — пользователь хочет добавить новую задачу в список.</p><ol><li>Пользователь вводит новую задачу в поле ввода через View.</li><li>View автоматически передает данные в ViewModel через двустороннюю привязку.</li><li>ViewModel обновляет Model, например, заносит новую задачу в базу данных.</li><li>Model говорит ViewModel, что данные изменились.</li><li>ViewModel обновляет View, и на экране появляется новая задача, но без явного вызова апдейта интерфейса.</li></ol><h3>MVP (Model-View-Presenter)</h3><p>В этом паттерне лежат: Model, View — здесь это пассивный UI, Presenter — посредник между View и Model, плюс в нем есть логика отображения. Работает это вот так:</p><figure><img src="https://media.tproger.ru/user-uploads/101078/2025-02-21/fb3613d5-574d-4a65-bb78-6be35440b3d3.png" alt="" /></figure><p>Пример — приложение, где есть чат.</p><ol><li>Пользователь набирает сообщение и нажимает «Отправить» или просто кнопку через View.</li><li>View передает этот текст в Presenter, и он вызывает Model, который отправляет сообщение на сервер.</li><li>Когда сообщение отправлено, сервер возвращает подтверждение Model, а он говорит об этом Presenter.</li><li>Presenter обновляет View, добавляя это сообщение в интерфейс.</li><li>Когда приходит новое сообщение от друга/коллеги, Model получает его из сети и передает Presenter.</li><li>Presenter обновляет View и отображает новое сообщение в чате.</li></ol><blockquote>Мы используем MVC при разработке бэкенда, MVVM — при кодинге на Angular и Vue, а MVP — для разработки UI на Android.</blockquote><p>А для управления данными можно использовать:</p><ul><li>Repository — абстрагирует доступ к данным и минимизирует зависимость от конкретной базы данных.</li></ul><blockquote>Важно разделять Repository на несколько интерфейсов (interface segregation). Для транзакций в распределенных системах можно использовать Saga или outbox + event bus.</blockquote><ul><li>Unit of Work — контролирует транзакции на уровне логики.</li><li>Active Record — связывает объекты с таблицами баз данных и просто упрощает работу.</li></ul><h2>Паттерны распределительных систем</h2><p>Микросервисы, Event-driven архитектура и CQRS помогают создавать масштабируемые и отказоустойчивые приложения. Рассмотрим их подробнее.</p><h3>Микросервисы</h3><p>В микросервисах приложение разбивается на небольшие, независимые сервисы, каждый из которых отвечает за конкретную бизнес-логику. Сервисы взаимодействуют друг с другом через API (обычно это HTTP или gRPC).</p><p>Микросервисы позволяют развертывать и масштабировать каждый сервис по отдельности. Плюс они могут разрабатываться на разных технологиях, например, языках и базах данных.</p><h4>Пример</h4><p>В приложении онлайн-магазина могут быть такие сервисы — User/Client Service, Product Order Service и Payment Service. Очевидно, что такой паттерн подойдет только большим приложениям, поэтому использовать микросервисы ради того, чтобы использовать — бессмысленно. В таком случае лучше остановиться на монолите.</p><blockquote>На мой взгляд, самая частая проблема — согласованность данных. В монолите гораздо проще её обеспечить средствами единого адресного пространства. Если мы говорим про подходы к декомпозиции, обычно используют декомпозицию по данным или доменной логике.</blockquote><p>Чтобы микросервисы работали четко, нужно грамотно организовать мониторинг, логирование и управление конфигами. Плюс нужен Kubernetes и API-шлюзы.</p><h3>Event-driven архитектура</h3><p>Это один из главных трендов последних лет. Подробнее о этой архитектуре можно почитать в нашей <a href="https://tproger.ru/articles/event-driven-arhitektura--principy-i-primery-ispolzovaniya">статье</a>.</p><p>Event-driven архитектура — это подход, при котором компоненты системы взаимодействуют через события. Сами события генерируются при изменении состояния системы и обрабатываются нужными компонентами.</p><p>Другими словами, все работает ассинхронно, то есть компоненты не блокируют друг друга, ожидая ответа — они буквально не знают друг о друге, а только реагируют на события. Плюс можно легко масштабировать и добавлять новые обработчики событий.</p><h4>Пример</h4><p>Тот же самый интернет-магазин:</p><ol><li>Пользователь создает заказ (создается событие).</li><li>Сервис уведомлений отправляет, например, пуш или письмо юзеру.</li><li>Сервис склада бронирует товары.</li><li>Сервис оплаты проводит транзакцию.</li></ol><h3>CQRS (Command Query Responsibility Segregation)</h3><p>CQRS — это паттерн, который разделяет логику команд и запросов. Для чтения (Query) и записи (Command) используются разные модели данных — так можно оптимизировать каждую операцию.</p><p>Архитектура здесь будет непростая, поэтому использовать CQRS нужно в приложениях, где будет высокая нагрузка на чтение.</p><h4>Пример</h4><p>Это может быть большой блог или медиа. Работать будет так:</p><ul><li>Через Command будут добавляться новые статьи или обновляться предыдущие.</li><li>Через Query будет, например, происходить поиск по ключевым словам или собираться список статей по тегам.</li></ul><p>Эти паттерны часто используются вместе. Например, в микросервисной архитектуре можно применять Event-driven подход, чтобы взаимодействовать между сервсами, а CQRS — для оптимизации операций чтения и записи.</p><h2>Паттерны для масштабируемости</h2><p>Если у вас падает один компонент, он не должен тянуть за собой всю систему и влиять на пользователей. Для этого есть паттерны, которые повышают надежность приложения.</p><h3>Circuit Breaker</h3><p>Этот паттерн работает как электрический предохранитель: если в одном из сервисов вылезла ошибка, например, он медленно отвечает, Circuit Breaker разрывает соединение и предотвращает перегрузку всей системы и каскадные сбои. Так, система не продолжает отправлять неудачные запросы, а переключается в режим ожидания, и затем периодически проверяет, работает ли сервис. Подходит для распределенных систем, где один упавший сервис может повлиять на всю цепочку вызовов.</p><p>Например, вы делаете покупку на маркетплейсе, и у в нем упал платежный сервис. В таком случае Circuit Breaker временно блокирует запросы и говорит пользователю, что нужно попробовать оплатить позже.</p><h3>Load Balancer</h3><p>Этот паттерн помогает равномерно распределять нагрузку и логический роутинг, предотвращая перегрузку отдельных узлов. Он следит за доступностью сервисов и отправляет запросы только к работающим компонентам. Особенно подходит облачным средах, где количество активных серверов может динамически изменяться.</p><p>Например, в стриминговом сервисе Load Balancer распределяет пользователей между разными серверами, чтобы не было перегрузки и они могли спокойно смотреть контент без задержек.</p><h3>Retry (exponential backoff with jitter)</h3><p>Паттерн нужен для повторения идемпотентного действия, которое завершилось безуспешно. Так, некоторые ошибки бывают кратковременными, и вместо немедленной блокировки запроса, Retry позволяет повторить запрос, но с увеличенным интервалом между попытками (экспоненциальная задержка). А добавление случайного разброса времени (jitter) помогает избежать ситуации, когда все запросы повторяются одновременно, вызывая новую перегрузку.</p><p>Например, вы пытаетесь купить билеты в театр на большую премьеру, но вместе с вами на сайте сидят еще 10 000 человек. Сервер временно перегружен, но система не сразу выдаст ошибку, а будет пытаться найти вам хотя бы какое-то место (где-то на последнем ряду балкона).</p><h2>Паттерны безопасности</h2><ul><li><b>OAuth 2.0</b> — стандартное решение. Он позволяет пользователям предоставлять доступ к своим данным сторонним сервисам без передачи пароля. Например, зарегистрироваться на сайте через Google, Яндекс или VK.</li><li><b>TLS (Transport Layer Security)</b>, криптографический протокол, шифрует данные при передаче.</li><li><b>AES</b> один из самых надежных алгоритмов симметричного шифрования. Его часто используют в базах данных или кеше.</li><li><b>Token-based Security</b> — безопасная аутентификация с JWT. В основе лежит цифровая подпись (обычно HMAC или RSA) — она гарантирует подлинность данных.</li><li>В финтехе используют <b>HSM</b> для генерации и защиты криптографических ключей на аппаратном уровне.</li></ul><p>Выбор паттерна — компромисс между гибкостью, производительностью и надежностью. Главное — учитывать требования бизнеса и технические ограничения. Плюс сейчас практически везде есть искусственный интеллект и облачные технологии, которые позволяют автоматизировать инфраструктуру, например, с помощью Terraform — о них тоже не нужно забывать.</p><p>И поскольку даже опытные разработчики сталкиваются с дилеммами при выборе подходящей модели, <a href="https://solvery.io/?utm_source=article&amp;utm_medium=partner&amp;utm_term=tproger&amp;utm_content=patterny_razrabotki&amp;utm_campaign=main_page">на Solvery</a> вы можете разобрать ваш проект и получить обратную связь от архитекторов и разработчиков уровня Яндекса, Авито и Т-Банка.</p><p>Если хотите сохранить короткую версию статьи — <a href="https://vk.com/wall-30666517_1857999">у нас есть она в ВК.</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Монолит или микросервисы: какую архитектуру выбрать для нового проекта</title>
      <link>https://tproger.ru/articles/monolit-ili-mikroservisy--kak-vybrat-arhitekturu-dlya-novogo-proekta</link>
      <comments>https://tproger.ru/articles/monolit-ili-mikroservisy--kak-vybrat-arhitekturu-dlya-novogo-proekta?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/monolit-ili-mikroservisy--kak-vybrat-arhitekturu-dlya-novogo-proekta</guid>
      <description><![CDATA[<p>Монолит или микросервисы. Показываем основные критерии выбора архитектуры нового проекта. Рассматриваем плюсы и минусы ✔ Tproger
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/monolit-ili-mikroservisy--kak-vybrat-arhitekturu-dlya-novogo-proekta">Монолит или микросервисы: какую архитектуру выбрать для нового проекта</a>»</p>]]></description>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 02 Dec 2024 15:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>При создании нового проекта выбрать правильную архитектуру необходимо, ведь это влияет на скорость разработки, масштабируемость и долгосрочную поддержку системы. Монолитная архитектура обеспечивает простоту и целостность, но может быть сложной в масштабировании. Микросервисы обеспечивают гибкость и независимость компонентов, но требуют продуманного подхода к интеграции и управлению. Сегодня рассмотрим разницу между монолитом и микросервисами, ключевые особенности каждой архитектуры и поможем вам сделать осознанный выбор.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-11-27/499fddb7-3212-4a4f-ab60-a65d318ffd08.png" alt="" /></figure><h2>Определение и особенности монолитной архитектуры</h2><h3>Что такое монолитная архитектура и какие у неё основные характеристики</h3><p>Монолитная архитектура представляет собой единую программную систему, в которой все модули и компоненты приложения тесно связаны и работают как единое целое. Вся логика приложения — от пользовательского интерфейса до базы данных — включена в единое приложение, которое развертывается и управляется как единое целое.</p><p><b>Основные характеристики:</b></p><ul><li>Единое хранилище кода и структур данных.</li><li>Тесная взаимосвязь компонентов: изменения в одном модуле часто требуют обновлений в других.</li><li>Унифицированное развертывание: приложение разворачивается в одном контейнере или среде.</li></ul><h3>Плюсы монолита</h3><ul><li><b>Простота разработки.</b> Все компоненты находятся в одном репозитории, что упрощает работу команды. Так, разработчики могут сосредоточиться на функциональности, а не на взаимодействии между сервисами. Плюс разработчики быстрее ориентируются в архитектуре.</li><li><b>Единое развертывание. </b>Нет необходимости управлять множеством сервисов, а обновление системы требует лишь одной операции деплоя.</li><li><b>Легкость тестирования. </b>Общее тестирование всех компонентов можно проводить в одной среде. А еще проще реализовать интеграционные и функциональные тесты.</li><li><b>Удобство в небольших командах</b>. Подходит для стартапов или проектов с ограниченными ресурсами, где важно быстро приступить к работе.</li></ul><h3>Минусы монолита</h3><ul><li><b>Сложность масштабирования.</b> Масштабирование требует копирования всего приложения, даже если нужно повысить производительность только одной его части. Плюс если высокую нагрузку испытывает лишь одна часть приложения, увеличивать производительность приходится для всей системы.</li><li><b>Зависимость компонентов.</b> Любое изменение в одном модуле может привести к непредсказуемым последствиям в других частях системы. К тому же трудно изолировать ошибки и снижать взаимные влияния между частями приложения.</li><li><b>Долгосрочная поддержка. </b>Переход на новые технологии требует переработки всей системы. Например, если монолитная архитектура построена на устаревшей версии фреймворка, переход на более современный инструмент требует изменений во всех связанных модулях, включая базу данных, пользовательский интерфейс и API.</li><li><b>Риск единой точки отказа.</b> Ошибка в одном компоненте может привести к полной остановке всего приложения.</li></ul><p>Монолитная архитектура подходит для небольших и средних проектов с ограниченными требованиями к масштабированию, где важны скорость разработки и простота управления. Однако для крупных динамично развивающихся систем она может стать сдерживающим фактором.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-11-27/fb74667c-b881-4215-98fa-d22b2d4b83e4.jpg" alt="" /></figure><h2>Определение и особенности микросервисной архитектуры</h2><h3>Что такое микросервисная архитектура и её ключевые аспекты</h3><p>Микросервисы как архитектура представляет собой подход, при котором приложение разбивается на независимые модули (микросервисы). Каждый микросервис выполняет определённую бизнес-функцию, имеет собственную базу данных (или доступ к её сегменту) и взаимодействует с другими через API.</p><p><b>Ключевые аспекты:</b></p><ul><li>Микросервисы разрабатываются, развёртываются и масштабируются отдельно.</li><li>Каждый микросервис отвечает за конкретную бизнес-логику.</li><li>Микросервисы могут использовать различные языки программирования, базы данных и фреймворки, что позволяет выбрать оптимальный инструмент для каждой задачи.</li></ul><h3>Плюсы микросервисов</h3><ul><li><b>Гибкость. </b>Микросервисы можно развивать независимо друг от друга. Так, в музыкальные стриминги (по типу Яндекс.музыки) новый функционал приложения можно добавлять без изменения сервиса, отвечающего за управление библиотекой треков.</li><li><b>Масштабируемость.</b> Каждый микросервис масштабируется отдельно в зависимости от нагрузки. Не нужно тратить время и мощности но прокачку одних и тех же функций одновременно, а сосредоточиться на приоритетных.</li><li><b>Независимое развертывание.</b> Обновление одного микросервиса не требует остановки всей системы. Например, если в приложении нужно обновить модуль расчёта стоимости (если мы разрабатываем Ozon или WB), это не повлияет на работу модуля оформления заказов.</li></ul><h3>Минусы микросервисов</h3><ul><li><b>Сложность в настройке.</b> Организация микросервисной архитектуры требует грамотной настройки инфраструктуры. Например, необходимо настроить API-шлюз для управления запросами, систему контейнеризации (например, Docker) и оркестрацию (Kubernetes).</li><li><b>Необходимость управления сетью. </b>Микросервисы взаимодействуют через сеть, что может приводить к задержкам и ошибкам. Например, если сервис обработки платежей не отвечает из-за проблем с сетью, процесс оформления заказа на том же Озоне может быть временно недоступен.</li><li><b>Сложности в тестировании.</b> Интеграционное тестирование требует симуляции взаимодействия между десятками или сотнями сервисов.</li></ul><p>Микросервисная архитектура идеально подходит для крупных проектов или систем, которым требуется гибкость и масштабируемость. Она позволяет разделить приложение на независимые компоненты, что упрощает развитие и обновление системы, особенно в условиях изменяющихся бизнес-требований.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-11-27/990eee10-701c-4b1c-805a-4e8e3a9d5e32.webp" alt="" /></figure><h2>Сравнение монолита и микросервисов</h2><p>При выборе архитектуры важно учитывать ключевые аспекты, которые влияют на производительность, масштабируемость, удобство поддержки и организацию командной работы. Поговорим об отличиях монолитов от микросервисов.</p><p>Производительность: какая архитектура быстрее и почему</p><p><b>Монолит</b></p><p>Монолитные приложения выигрывают в производительности благодаря отсутствию сетевых вызовов между компонентами. Например, если в приложении есть функция расчёта скидки и оформления заказа, в монолите эти операции выполняются быстрее, так как всё обрабатывается в едином контексте без дополнительных задержек.</p><p><b>Микросервисы</b></p><p>Микросервисы могут быть медленнее из-за сетевых взаимодействий между компонентами. В приложении с такой архитектурой расчёт скидки может быть отдельным сервисом, и каждый запрос к нему потребует сетевого соединения. Однако микросервисы позволяют распределять нагрузку, обеспечивая высокую производительность при большом количестве пользователей.</p><p>Масштабируемость: вертикальное и горизонтальное масштабирование</p><p><b>Монолит</b></p><p>Плюс монолита в том, что его масштабирование осуществляется вертикально (больше оперативной памяти —&gt; мощнее процессор). Это быстро, но дорого, и есть физические ограничения на увеличение ресурсов.</p><p><b>Микросервисы</b></p><p>Поддерживают горизонтальное масштабирование, позволяя увеличивать производительность за счёт запуска дополнительных копий только нужных сервисов. Например, если нагрузка растёт на модуль обработки платежей, администратор может увеличить количество инстансов только этого сервиса, не затрагивая остальные части приложения.</p><p>Поддержка и обновление: как быстро можно добавлять новые функции</p><p><b>Монолит</b></p><p>В монолите изменения требуют работы с общей кодовой базой. Так, добавление нового фильтра в поисковую систему потребует изменений в коде и обязательного тестирования всего приложения. Это может замедлить внедрение обновлений, особенно в сложных системах.</p><p><b>Микросервисы</b></p><p>Каждый микросервис разрабатывается и обновляется независимо. Например, добавление нового фильтра в ту же поисковую систему можно реализовать как отдельный сервис, что не затронет работу других компонентов. Это позволяет быстрее развивать функциональность приложения.</p><p>Командная работа: как архитектура влияет на распределение задач в команде</p><p><b>Монолит</b></p><p>Подходит для небольших команд, так как вся разработка сосредоточена в одной кодовой базе. Однако в крупных командах может возникать путаница: одновременно работать над различными функциями сложно из-за пересечения задач и конфликтов.</p><p><b>Микросервисы</b></p><p>Позволяют разделить задачи между командами, так как каждая  отвечает за свой микросервис. Например, одна команда может работать над модулем аналитики, а другая — над модулем рекомендаций, не мешая друг другу. Это делает микросервисы идеальными для крупных проектов с распределёнными командами.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-11-27/a1c18a40-2d4e-4718-9690-a9d23d3f962e.png" alt="" /></figure><h2>Факторы, которые влияют на выбор архитектуры</h2><p>Выбор между монолитной и микросервисной архитектурой зависит от множества факторов, например, особенностей команды, масштаба проекта, технических требований и доступных ресурсов. Так что же выбрать?</p><h3>Размер и опыт команды разработчиков</h3><p><b>Маленькая команда</b></p><p>Если команда небольшая и состоит из 3–5 человек, лучше начать с монолитной архитектуры. Например, для стартапа с ограниченным бюджетом разработка единой кодовой базы будет быстрее и эффективнее, так как все участники смогут работать в одном контексте без разделения на сервисы.</p><p><b>Большая команда</b></p><p>В случае крупной команды, где каждая группа имеет узкую специализацию (например, фронтенд, бэкенд, аналитика), микросервисы становятся более подходящим решением. Так, отдельная команда может заниматься разработкой API для управления пользователями, не мешая работе команды, отвечающей за обработку платежей.</p><p><b>Уровень опыта</b></p><p>Новичкам проще начать с монолита, где всё сосредоточено в одном месте. Микросервисы требуют знания работы с сетями, управления контейнерами и настройки взаимодействия сервисов, что может быть сложно для начинающих.</p><h3>Сложность и масштаб проекта</h3><p><b>Простые проекты</b></p><p>Для небольших приложений монолитная архитектура подходит идеально.</p><p><b>Сложные и крупные проекты</b></p><p>Если проект включает множество связанных функций, типа интернет-магазина с рекомендациями, платежной системой, логистикой и аналитикой, микросервисы становятся более предпочтительными. Разделение функционала на отдельные сервисы позволяет легче масштабировать и обновлять отдельные части системы.</p><h3>Требования к производительности и масштабируемости</h3><p><b>Низкие требования</b></p><p>Если приложение обслуживает ограниченное количество пользователей, например, внутренний инструмент для команды из 50 человек, монолитная архитектура будет более эффективной. Она обеспечит стабильную производительность без сложных настроек.</p><p><b>Высокие требования</b></p><p>Для приложений с миллионами пользователей микросервисы обеспечивают гибкость и возможность горизонтального масштабирования. Например, если растёт нагрузка на видео-хостинг, можно увеличить количество экземпляров только сервиса трансляции.</p><h3>Планы на будущее и возможность роста проекта</h3><p><b>Краткосрочные проекты</b></p><p>Для приложений с коротким жизненным циклом лучше выбрать монолит. Например, MVP для тестирования идеи легче разработать именно так, поскольку это позволяет быстрее выйти на рынок.</p><p><b>Долгосрочные проекты</b></p><p>Если приложение предполагается развивать на протяжении многих лет с добавлением новых функций, микросервисы могут быть более удобны. Например, в крупной ERP-системе с постоянным расширением функционала проще добавлять новые сервисы, не нарушая работу существующих.</p><h3>Инфраструктура и доступные ресурсы</h3><p><b>Ограниченная инфраструктура</b></p><p>Монолитные приложения не требуют сложной инфраструктуры. Их можно развернуть на одном сервере и минимизировать затраты на администрирование.</p><p><b>Развитая инфраструктура</b></p><p>Вам нужно настроить архитектуру, и микросервисы в этом случае потребуют контейнеризации (Docker), оркестрации (Kubernetes) и мощных инструментов мониторинга.</p><p>Выбор архитектуры зависит от целей и условий проекта. Для небольших приложений с ограниченными ресурсами и командами монолит — быстрый и эффективный выбор. Для крупных систем с перспективой роста и распределёнными командами микросервисы предлагают гибкость, масштабируемость и независимость в разработке. Важно учитывать баланс между текущими потребностями и будущими планами, чтобы архитектура оставалась жизнеспособной на протяжении всего жизненного цикла проекта.</p><h2>Кейсы, или практические советы по выбору архитектуры</h2><p>Попробуем подобрать подходящую архитектуру на конкретных кейсах. Для примера возьмем сферу электронной коммерции (раз уж мы много говорили про маркетплейсы).</p><h3>Когда стоит использовать монолит</h3><ul><li><b>Простые проекты.</b> Локальный магазин для продажи органической продукции, где пользователь может оформить заказ через простую корзину, а администратор получает уведомление на почту.</li><li><b>Стартапы с ограниченными ресурсами.</b> Новый бренд, продающий уникальные дизайнерские украшения, быстро запускает MVP с базовыми функциями: каталог, корзина, оплата и личный кабинет.</li><li><b>Приложения без высоких требований к масштабируемости.</b> Интернет-магазин для корпоративных клиентов, где покупки совершаются нерегулярно, и основная нагрузка на сервер минимальна.</li></ul><h3>Когда подходят микросервисы</h3><ul><li><b>Крупные проекты.</b> Большой интернет-магазин с тысячами товаров, сложной структурой категорий и разными модулями (каталог, поиск, рекомендации, платежи).</li><li><b>Распределённые команды.</b> Когда команда в одной стране разрабатывает сервис логистики, а команда в другой — систему интеграции с локальными платёжными провайдерами.</li><li><b>Приложения с высокой нагрузкой. </b>Маркетплейс, который обрабатывает миллионы запросов в секунду, поддерживает сложные фильтры, рекомендации и динамическое ценообразование.</li></ul><h2>Как перейти с монолита на микросервисы</h2><figure><img src="https://media.tproger.ru/user-uploads/102593/2024-11-27/1c2f47e4-31df-4ffa-9833-57beff3954f0.png" alt="" /></figure><h4>1. Оцениваем текущую архитектуру</h4><p>Для начала выявляем модули с наибольшей нагрузкой. Так, если монолитный интернет-магазин испытывает перебои из-за частых запросов к каталогу товаров, начинаем с выделения поиска в отдельный микросервис.</p><h4>2. Разрабатываем стратегию миграции</h4><p>Постепенно выносим модули, сохраняя функциональность. То есть сначала выносим модуль обработки платежей в отдельный сервис, затем переходим к модулям рекомендаций и аналитики.</p><h4>3. Обеспечиваем инфраструктуру</h4><p>Настраиваем контейнеризацию для запуска микросервисов (например, Docker для микросервисов каталога и управления заказами с общей базой данных).</p><h4>4. Используем очередь сообщений</h4><p>Реализуем асинхронное взаимодействие сервисов. Можем настроить очередь RabbitMQ для передачи данных о новых заказах от фронтэнда к микросервису логистики.</p><h4>5. Тестируем взаимодействие сервисов</h4><p>Постоянно проверяем корректность обмена данными. Так, тестируем модуль интеграции с внешними складами, чтобы убедиться в правильной обработке информации о наличии товаров.</p><h2>Примеры реальных проектов и их архитектурных решений</h2><h3>Примеры компаний, которые используют монолит и преуспели в этом</h3><h4>Basecamp</h4><p><a href="https://vizologi.com/basecamp-business-model-how-basecamp-works-makes-money/">Basecamp</a> —  популярный инструмент для управления проектами. Он известен своей монолитной архитектурой. Для команды Basecamp это решение значительно облегчает внедрение новых функций и упрощает развертывание, так как все компоненты находятся в одной кодовой базе.</p><h4>Shopify</h4><p><a href="https://mehmetozkaya.medium.com/shopifys-modular-monolithic-architecture-a-deep-dive-%EF%B8%8F-a2f88c172797">Shopify</a>, одна из крупнейших платформ для создания интернет-магазинов, на ранних этапах своего роста использовала монолитную архитектуру. В отличие от многих других компаний, которые переходят на микросервисы, Shopify создала гибкую систему в рамках монолита, которая поддерживает стабильность и масштабируемость, не перегружая инфраструктуру. Несмотря на это, они также внедряли элементы модульности в свой монолит, что позволяло сохранять производительность при росте компании​.</p><h4>GitHub</h4><p><a href="https://www.infoq.com/articles/github-monolith-microservices/">GitHub</a>, крупнейшая платформа для хостинга и совместной разработки ПО, также начинала с монолитной архитектуры. Этот подход позволил поддерживать высокую скорость разработки и эффективность работы небольшой команды. В дальнейшем GitHub внедрил стратегию масштабирования внутри своего монолита, что позволило им эффективно справляться с растущими требованиями без перехода на микросервисы на первых этапах​.</p><h3>Примеры компаний, которые выбрали микросервисную архитектуру</h3><h4>Netflix</h4><p><a href="https://netflixtechblog.com/">Для масштабируемости и высокой доступности</a> Netflix перешел от монолитной архитектуры к микросервисам. Это позволило им эффективно обрабатывать большое количество запросов пользователей и улучшить управление различными компонентами системы.</p><h4>Amazon</h4><p><a href="https://aws.amazon.com/ru/microservices/">Amazon</a>, особенно его платформа Amazon Web Services (AWS), также широко использует микросервисы. Переход к такой архитектуре позволил Amazon улучшить управление масштабируемостью, изоляцию сбоев и гибкость развертывания. Каждый сервис отвечает за отдельную задачу, что делает платформу более гибкой и облегчает масштабирование отдельных компонентов.</p><h2>Ошибки, которых стоит избегать при выборе и реализации архитектуры</h2><h4>1. Вы не понимаете требования</h4><p>Ошибки могут возникать, если архитектура выбирается без детального анализа потребностей проекта. Это может привести к излишней сложности и необходимости управления множеством сервисов, которые не нужны на первых этапах.</p><p><i>Совет: </i>Тщательно анализируйте требования к масштабируемости, безопасности, производительности и возможностям роста, прежде чем принимать решение о типе архитектуры.</p><h4>2. Вы не учитываете долгосрочные последствия</h4><p>При использовании монолитной архитектуры может быть сложно адаптировать систему под новые требования по мере роста. В то же время излишняя фрагментация в микросервисах приводит к значительным затратам на управление и координацию, особенно если проект не хочет масштабироваться.</p><p><i>Совет:</i> Планируйте архитектуру с учётом будущих потребностей и возможности масштабирования, но не усложняйте проект без реальной необходимости.</p><h4>3. Вы игнорируете опыт и возможности команды</h4><p>Выбор архитектуры должен учитывать не только требования проекта, но и опыт вашей команды. Чтобы развернуть микросервисы, в тиме должны быть опытные DevOps-специалисты. И в целом все сотрудники должны обладать сильными навыками для настройки, управления и мониторинга такой архитектуры.</p><p><i>Совет</i>: Оцените техническую зрелость и возможности вашей команды, чтобы понять, будет ли она в состоянии поддерживать сложную архитектуру.</p><h4>4. У вас проблемы с тестированием</h4><p>При использовании микросервисной архитектуры сложнее поддерживать тестирование, так как необходимо чекать не только отдельные сервисы, но и взаимодействие между ними. В монолитной архитектуре тестирование проще, но с увеличением объема кода и сложности системы это может стать проблемой.</p><p><i>Совет:</i> Убедитесь, что для выбранной архитектуры предусмотрены необходимые стратегии тестирования, чтобы минимизировать риски ошибок при развертывании.</p><h4>5. Вы не уделяете внимание безопасности</h4><p>Независимо от выбранной архитектуры, безопасность всегда должна быть приоритетом. Микросервисы, в свою очередь, увеличивают поверхность атаки из-за множества сервисов, а монолит может быть уязвимым, если не принято должное внимание к его изоляции и защите данных.</p><p><i>Совет</i>: Включите в архитектурное проектирование практики безопасности с самого начала, независимо от того, выбрали вы монолит или микросервисы.</p><p>Эти ошибки можно избежать, тщательно планируя архитектуру, принимая во внимание как текущие потребности, так и потенциальные вызовы в будущем.</p>]]></content:encoded>
    </item>
    <item>
      <title>Обеспечение низких задержек в высоконагруженной системе: разбор реального кейса</title>
      <link>https://tproger.ru/articles/obespechenie-nizkih-zaderzhek-v-vysokonagruzhennoj-sisteme--razbor-realnogo-kejsa</link>
      <comments>https://tproger.ru/articles/obespechenie-nizkih-zaderzhek-v-vysokonagruzhennoj-sisteme--razbor-realnogo-kejsa?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Максим Юнусов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/obespechenie-nizkih-zaderzhek-v-vysokonagruzhennoj-sisteme--razbor-realnogo-kejsa</guid>
      <description><![CDATA[<p>Как разработчику обеспечить низкие задержки сложной системы при высоких нагрузках: поделился методологией решения задачи.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/obespechenie-nizkih-zaderzhek-v-vysokonagruzhennoj-sisteme--razbor-realnogo-kejsa">Обеспечение низких задержек в высоконагруженной системе: разбор реального кейса</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 07 Feb 2024 08:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Как разработчику обеспечить низкие задержки сложной системы при высоких нагрузках? <b>Ведущий эксперт </b><b>IT</b><b>_</b><b>ONE</b><b> </b><b>Максим Юнусов</b> делится методологией для решения этой задачи, на примере реального проекта: модернизации Подсистемы обеспечения доступа к данным федеральной государственной информационной системы «Единая система межведомственного электронного взаимодействия» (ПОДД СМЭВ 4). <i></i></p><h2>Постановка задачи</h2><p>СМЭВ – это система, обеспечивающая электронное взаимодействие различных органов государственной власти. Предполагается, что в дальнейшем к ней могут быть подключены в качестве поставщиков и другие участники: коммерческие организации и частные лица. Доступ пользователей (потребителей данных) к информации поставщиков данных внутри системы осуществляется в формате Витрин данных – специализированных наборов структурированных данных. Соответственно, ПОДД СМЭВ – это компонент транспортной подсистемы СМЭВ, обеспечивающий доступ к данным, размещенным на Витринах данных. Предполагается, что ПОДД должна исполнять различные сложные запросы.</p><p>Итак, архитектурно ПОДД до трансформации – распределенная система из множества связанных между собой компонентов. Ее ядром является планировщик запросов на базе Apache Calcite, а основной протокол взаимодействия компонентов – журнал на базе Apache Pulsar, обеспечивающий гарантированность доставки. Механизм ограничения нагрузки (Rate Limiting) защищает систему от DoS-атак. Обработка запроса включает разбор запроса, производимый при каждом вызове. С целью обеспечить гарантированную доставку все сообщения сохраняются на диск.</p><p>До трансформации система поддерживала 1000 активных пользователей одновременно, время отклика 1 секунду (в 99% случаев) и мощность (пропускную способность) 5000 запросов в секунду. В соответствии с ТЗ заказчика, нам необходимо было получить следующие характеристики: 25000 активных пользователей, мощность 30000 запросов в секунду и время отклика не более 0,5 секунд на 99%.</p><figure><img src="https://media.tproger.ru/user-uploads/97489/2024-02-05/584d5255-4582-4954-8cb6-86a86b975338.png" alt="" /><figcaption>Архитектура ПОДД до трансформации</figcaption></figure><p>Среди большого количества дополнительных ограничений необходимо отметить два: реализацию проекта на имеющемся «железе» и очень короткие сроки – один квартал. Таким образом, классическое решение задачи производительности – «затопить железом» – отпало само собой. Как и распространенная эволюционная модель: «попробовал, проверил, если не получилось – переделал». Надо было сразу спроектировать систему таким образом, чтобы уже на этапе планирования архитектура решения оказалась приемлемой по стоимости и обеспечивала ожидаемые показатели.</p><h2>Этап 1: сдвигаем «бутылочное горлышко»</h2><p>Предельные значения пропускной способности и времени отклика определяются максимальной потребностью в ресурсе – то есть временем, проведенном задачей на аппаратном ресурсе. Тот ресурс, который более всего замедляет систему, становится так называемым «бутылочным горлышком», задавая показатели производительности, выше которых подняться невозможно. Это основная проблема любой высоконагруженной системы, поэтому авторы учебников рекомендуют начинать ее оптимизацию именно с поиска «бутылочного горлышка». Оптимизация этого ресурса дает существенно больший выигрыш в производительности, чем оптимизация любого другого компонента системы.</p><p>Классический алгоритм действий для решения этой задачи – принцип центрирования. Находим «бутылочное горлышко» по симптомам: максимальная утилизация ресурса, максимальная потребность в ресурсе (время пребывания в ресурсе за один запрос), максимальная очередь к ресурсу. В нашем случае оно было очевидным: Apache Calcite. Обработчик запросов и тормозил систему больше всего. Для уменьшения потребности в этом ресурсе мы последовательно рассмотрели несколько тактик.</p><p>Первая – оптимизация алгоритма. Это был бы хороший заход, если бы Calcite писался нами «с нуля». Calcite не ускорить и сложно заменить, переписать его можно, но очень дорого. Мы сделали из него практически собственный форк, но в итоге уперлись в некоторые основные механизмы, которые переписывать не рискнули. Таким образом, эффективность здесь не особо работает.</p><p>Вторая – переиспользование ресурсов и результатов, кэширование. Это тоже отличная тактика, но в нашем случае она не работает, так как запросы, поступающие в систему, – уникальные, и кэширование не дает никакого эффекта.</p><p>Третья – уменьшение накладных расходов, то есть экономия на транспорте. Вариант приемлем, когда в системе много времени уходит на взаимодействия, всевозможные перекладывания, перекодирование и т. д. Здесь он не работает, так как в рамках Calcite накладных расходов просто нет.</p><p>Четвертая – раннее связывание. Подход здесь следующий: если большинство запросов регламентированы, то есть SQL известны заранее, мы можем разобрать их еще до выполнения, а потом просто брать их с диска и выполнять.</p><p>Эту тактику мы и выбрали. Убрали разбор и планирование запроса Calcite с основного пути, передвинув его в область «особых случаев» (паттерн Special Cases). То есть, когда система получит неизвестный SQL, то погонит его через Calcite. В остальных случаях планы запросов составляются заранее и берется уже готовый, закэшированный план выполнения. Это существенно сократило время выполнения запросов.</p><p>К сожалению, «бутылочное горлышко» полностью устранить нельзя, его можно только передвинуть. В нашем случае, решив проблему с Calcite, мы уперлись в следующую проблему: механизм проверки подписи входящих сообщений «КриптоПро». С ним не работает ни одна тактика оптимизации, поэтому данное «бутылочное горлышко» нам пришлось оставить.</p><figure><img src="https://media.tproger.ru/user-uploads/97489/2024-02-05/3003c072-b643-48d2-b672-8fdf3afd2a24.png" alt="" /><figcaption>Сдвигаем бутылочное горлышко</figcaption></figure><p>Но уже на этом этапе мы добились времени отклика примерно 300 микросекунд на основных сервисах и пропускной способности 10000 запросов в секунду. Количество одновременных активных пользователей осталась прежней – 1000.</p><h2>Оптимизация «надсистемы»</h2><p>Согласно методологии, если системе не удается больше ничего улучшить, нужно выходить в «надсистему» – то есть не бить «точечно» по сервисам, а смотреть на систему целиком. И на этом этапе на первый план выходит третья из перечисленных выше тактик – уменьшение накладных расходов. Здесь уже появляется сетевое взаимодействие, которое вполне можно оптимизировать.</p><p>По нашим подсчетам, на пути каждого запроса в системе происходит 6 взаимодействий, большинство из них это Apache Pulsar. Мы можем уменьшить накладные расходы прежде всего за счет снижения количества этих взаимодействий, а также за счет их облегчения самих по себе. Важное правило, которым я всегда пользуюсь: быстро бегущий «монолит» лучше группы микросервисов. Микросервисы – это не про производительность, а скорее про то, как быстро вывести фичу на рынок. Если же вы хотите избавиться от накладных расходов, сервисы придется укрупнять. Мы уменьшили количество соединений до 4, сэкономили несколько микросекунд, устранили избыточные сетевые задержки.</p><p>Чтобы сделать сами соединения легче, мы обратили внимание на дисковые операции. Дело в том, что Pulsar каждое сообщение пишет на диск. Делает он это быстро и эффективно, но в силу своих особенностей не умеет так же быстро и эффективно стирать вычитанные сообщения. Вследствие этого дисковое пространство под нагрузкой переполнялось очень быстро, отсюда и возникало ограничение количества активных пользователей.</p><p>Разумное решение – убрать сохранение на диск, заменив Pulsar механизмом RSocket. В итоге брокер у нас остался, но самописный и легкий. Потоковое взаимодействие обеспечивается поверх других взаимодействий, а значит, если мы один раз установили соединение, то потом не тратим время на его переустановление.</p><p>Однако при такой схеме мы теряем важную характеристику – гарантированную доставку, которая должна обеспечить надежность системы на уровне 99,999% (пять девяток). Поэтому вместо сохранения всех сообщений на диске мы применяем схему с надежным подписчиком (Durable Subscriber): то есть прогоняем все запросы через память, но если кто-то на этом пути запрос не забрал, система отправляет его на диск и поднимает оттуда, когда Агент ПОДД заново вычитывает свои сообщения. Для реализации этой схемы мы использовали инструмент BookKeeper, который остался после Pulsar.</p><figure><img src="https://media.tproger.ru/user-uploads/97489/2024-02-05/2eeab102-f306-44d0-885a-227f75d2d896.png" alt="" /><figcaption>Оптимизация «надсистемы»</figcaption></figure><p>Результат – система поддерживает одновременную работу 30000 активных участников, сквозное время отклика – 20 микросекунд. Пропускная способность сохраняется с первого этапа: 10000 запросов в секунду. Напомним, что нам нужно поднять ее до 30000.</p><h2>Разумное масштабирование</h2><p>Поскольку на этапе оптимизации «бутылочного горлышка» мы уперлись в «КриптоПро», решение задачи по увеличению пропускной способности системы теперь будет связано с масштабированием – добавлением новых инстансов сервиса. Расчет необходимого количества элементов для масштабирования с запасом показал, что количество инстансов необходимо увеличить в 4 раза.</p><p>Однако на этом задача на практике не исчерпывается. Дело в том, что масштабирование – это не только количество «железа», но и, в том числе, грамотная балансировка. Несбалансированная обработка нивелирует эффективность масштабирования – ведь если балансировщик все запросы будет отправлять на один инстанс, все остальные будут бездействовать. Так, сервис K8s с настройками по умолчанию не выполняет балансировку запросов на обслуживаемые сервисы. Когда создается TCP-соединение, оно проходит через правила iptables, которые и выбирают конкретный pod бэкенда, куда попадет трафик. Поскольку все следующие запросы идут уже по открытому TCP-соединению, правила iptables больше не вызываются.</p><p>Нужен грамотный балансировщик, который правильно распределяет задачи по инстансам, желательно используя понимание размеров и веса этих задач – так называемое умное планирование. «Коробочных» решений для этого, к сожалению, нет. Надо либо дорабатывать Kubernetes, либо самим разрабатывать решение. Мы остановились на втором варианте: использовали клиентскую балансировку Join-Shortest-Queue (JSQ) на хост с минимальной очередью. Данная балансировка самая простая с точки зрения реализации – эффективный, хотя и не идеальный алгоритм. С точки зрения времени отклика здесь идеально смотрелся бы алгоритм SITA (Size-Interval Splitting), но он может привести к недоутилизации и снижению пропускной способности.</p><figure><img src="https://media.tproger.ru/user-uploads/97489/2024-02-05/7d60ccb8-cd68-466d-adec-b5a3da65c8b1.png" alt="" /><figcaption>Балансировка нагрузки</figcaption></figure><p>А как же закон Амдала? Ведь, согласно нему, масштабирование не всегда приводит к успеху: бывает, что производительность падает с увеличением количества инстансов. Допустим, мы выдержим нагрузку на четырех инстансах – а какова предельная мощность системы? Для расчета максимально возможного количества инстансов мы используем не формулу Эрланга 2-го рода (в данном случае слишком сложную), а универсальный закон масштабируемости Гюнтера, USL. Эта формула дает более приблизительные ответы, но они достаточны для предварительной оценки. Закон рассматривает два коэффициента, влияющие на падение производительности: «штраф конкурентности» (contention penalty) и «штраф когерентности» (coherence penalty). Учитывая их, мы вычислили, что в нашей схеме производительность будет падать при масштабировании примерно до 10 инстансов.</p><p>Также мы нашли участника нашего взаимодействия который ограничивал наши сервисы по взаимодействию – это сервис блокировок. Без Rate Limiter в асинхронной системе не обойтись: если не ограничивать каким-то образом поток, систему рано или поздно «затопит». Если мы хотим держаться в рамках приемлемого Latency, мы должны это контролировать. В то же время существуют другие механизмы контроля потока, не только ограничение пропускной способности. В частности, контроль длины очереди возможен через организацию обратного давления (Back Pressure).</p><p>Таким образом, мы контролируем длину очереди, организуя обратное давление на агентов – просто не пускаем их в систему, пока длина очереди не станет приемлемой. Таким образом обеспечивается работоспособность системы целиком. Rate Limit в системе остался, но за пределами основного контура.</p><h2>Результат</h2><p>В результате проекта модернизации системы ПОДД мы добились целевых показателей и даже существенно перевыполнили их: 30000 активных участников (цель по ТЗ – 25000), мощность – 60000 запросов в секунду на 4 инстансах (цель – 30000), время отклика – 0,2 сек в 99% случаях (цель – 0,5 сек).</p><figure><img src="https://media.tproger.ru/user-uploads/97489/2024-02-05/01154b24-370c-436d-81e5-411bfc9294f9.png" alt="" /><figcaption>Архитектура ПОДД после трансформации</figcaption></figure><p>Перечислю основные результаты нашей работы:</p><ul><li>распределенная система из нескольких слабосвязанных компонентов;</li><li>оптимизированный брокер сообщений на базе протокола Rsocket;</li><li>предварительный расчет планов обработки с использованием Apache Calcite;</li><li>сохранение сообщений на диск осуществляется только при невозможности доставки;</li><li>оперативные данные не привязаны к конкретным экземплярам сервисов;</li><li>балансировка по наименьшей очереди;</li><li>обратное давление.</li></ul><p>Важно, что все перечисленные шаги к достижению цели были произведены согласно эффективно выстроенной методологии, четко определенному поэтапному плану.</p><p>В качестве потенциальных доработок системы можно назвать добавление умного планирования очереди выполнения, перевод некоторых важных задач в фоновый режим, отдельную обработку непредсказуемых по времени обработки запросов. Систему можно улучшать с точки зрения производительности, но на данном этапе мы достигли целевых показателей, и остальные сценарии пока просто оставляем в уме.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как не допустить свалки в Django-проекте: MTV, services.py, новые приложения</title>
      <link>https://tproger.ru/articles/kak-ne-dopustit-svalki-v-django-proekte-mtv-services-py-novye-prilozheniya-239596</link>
      <comments>https://tproger.ru/articles/kak-ne-dopustit-svalki-v-django-proekte-mtv-services-py-novye-prilozheniya-239596?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Илья Осипов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ne-dopustit-svalki-v-django-proekte-mtv-services-py-novye-prilozheniya-239596</guid>
      <description><![CDATA[<p>В этом материале мы обсудим, как писать чистый код, и какие концепции и типовые ошибки превращают утончённые проекты в заросли и свалки.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ne-dopustit-svalki-v-django-proekte-mtv-services-py-novye-prilozheniya-239596">Как не допустить свалки в Django-проекте: MTV, services.py, новые приложения</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[Кодстайл]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 13 Apr 2023 13:31:38 GMT</pubDate>
      <content:encoded><![CDATA[<p>Привет! Меня зовут Илья Осипов, я методист курса программирования на Python «Девман» и больше пяти лет пишу код на этом языке. В этом материале мы обсудим концепции и типовые ошибки, которые превращают утончённые и «правильные» проекты в заросли и свалки.</p><h2>Когда вскрывается проблема</h2><p>Представьте, вы пишете проект на Django в команде из пяти человек. На старте все замотивированы «сделать хорошо», ведь это шанс сделать проект «идеальным» с самого начала. Но проходит год или два, проект обрастает парой десятков тысяч строк кода. Разрабатывать становится всё сложнее: файлы ломятся от кода, навигироваться по проекту без продвинутых инструментов из IDE просто невозможно.</p><p>В этот момент разработчики обычно говорят, что причина возрастающей сложности — рост проекта. Или грешат на «коллег-говнокодеров» / «плохой фреймворк». Хотя с ростом проекта навигация действительно естественным образом усложняется, чаще всего виноваты отнюдь не фреймворки или количество строчек, а плохие решения.</p><p><b>Основная цель фреймворка</b> — задать «фреймы», то есть «границы», в рамках которых будет работать программист. Для этого внутри Django предусмотрено множество механизмов, и многие из них описаны в документации. Некоторые — прямо в исходниках фреймворка. А какие-то детали разработчики Django не предусмотрели, и программистам приходится придумывать новые границы самостоятельно.</p><p>Зачем нужны эти границы? Разве без них не лучше?</p><p>Ответ на этот вопрос сходу поймёт любой пользователь фреймворка: не лучше. Границы пусть и создают дополнительные «препятствия», но взамен они обеспечивают понятное пространство для работы: сюда писать такой код, а сюда – вот такой, и всё будет работать. Не нужно каждый раз «изобретать велосипед» и пытаться придумать, как будет работать ваш сайт. За вас уже подумали и оставили «места для дописывания кода». Вот эти места для вашего кода — это и есть <b>фреймы</b>.</p><h2>Архитектура MTV</h2><p>Самый «высокоуровневый» фрейм в Django — это архитектурное разделение логики на Model, View и Template.</p><p><i>Да, архитектура Django называется именно MTV, а не MVT. Разработчики используют в документации именно такую аббревиатуру. По сути, архитектура почти полностью повторяет другой подход — MVC, Model-View-Controller (Модель-Представление-Контроллер).</i></p><p>Суть такая:</p><ul><li>в Model хранят данные сервиса;</li><li>View отвечает за отображение данных пользователю;</li><li>Controller отвечает за то, чтобы сообщать Model, когда пора менять данные.</li></ul><p>В Django же отошли от этой концепции и использовали архитектуру Model, View и Template. Сами разработчики Django <a href="https://docs.djangoproject.com/en/4.1/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names">пишут</a>, что не видят никакого конфликта с MVC. Model по-прежнему отвечает за хранение данных и работу с ними. View — за «отображение» данных. Просто этот слой они разделили на два: View отвечает за то, чтобы решить, какие данные отображать, а Template — как отображать. Куда делся Controller из MVC? В аббревиатуре MTV он пропал.</p><p>Разработчики считают, что это сам фреймворк.</p><p>Возможно, вы хотите спросить, зачем я об этом рассказываю. Разве возможно накосячить на этом уровне? Вообще-то, да.</p><h2>Бизнес-логика, или слой Services</h2><p>Многие разработчики пытаются переизобрести MTV и добавляют в Django дополнительный слой: Services. Вы найдёте файл serivces.py во многих туториалах и видео по Django.</p><p>Необходимость добавления этого слоя обычно объясняют так:</p><ul><li>В моём проекте появились функции, которые не укладываются ни в Model, ни во View;</li><li>Я хочу сделать «бизнес-логику» реюзабельной между приложениями, и, чтобы не импортировать функции из одного View в другой, храню их отдельно.</li></ul><p>Эти проблемы действительно существуют. Зачастую в проекте появляются вспомогательные функции, которые не принадлежат стандартным сущностям Django, вроде Model или View.</p><p>Но решение складывать весь этот код в файл services.py, на мой взгляд, плохое. Вот почему:</p><p><b>Слой Services слишком широкий, это плохая рамка.</b> При определении рамок важно не определить, какой код можно класть в этот файл, а решить, какой код в этот файл класть нельзя. Для сравнения, Model хорошо справляется с этой задачей: если код не относится к работе с БД, то ему не место в файле models.py. А в services.py часто кладут всё, что не влезло в остальные.</p><p><b>Файл становится свалкой.</b> Это ожидаемое следствие плохой рамки. Я часто интересуюсь у друзей и у кандидатов на собеседованиях, есть ли в их текущих или прошлых проектах такая проблема: в services.py более 2 тысяч строк кода и по нему тяжело навигироваться. Если в проекте более 10 тысяч строчек кода, разработчики почти единогласно говорят, что файл превращается в свалку.</p><p><b>Файл размывает слой View.</b> Есть мнение, что в файл services.py нужно выносить бизнес-логику. Термин «бизнес-логика» кажется конкретным — это всё, что определяется правилами бизнеса. Но на поверку он размыт куда сильнее, чем кажется. Разработчики, глядя на функцию, иногда не могут сказать, бизнес-логика это или нет.</p><h3>Куда девать общий код?</h3><p>Альтернативное решение — оставить фреймворк в покое и не добавлять в него новый слой. Если появилась новая логика, то создать файл с говорящим названием и положить код в него. Если логики вынести нужно много — создайте несколько файлов. Искать нужную функциональность проще в одном мегафайле на тысячу строк или в пяти небольших по двести строк? Так будет куда понятнее, где, например, искать функцию, которая форматирует дату или преобразует цену товара:</p><figure><img src="https://media.tproger.ru/uploads/2023/04/61bf9f7a-76ed-4de0-b53b-b63c82ccc89f.png" alt="" /></figure><h2>Писать запросы в слое View — это нормально</h2><p>Я так и не разобрался, откуда взялся миф о том, что во View нельзя писать запросы в БД, и в чём аргументация его сторонников. Это правило просто откуда-то «взялось», и теперь в него верят как в аксиому.</p><p>Тем не менее в документации Django для слоя View очень <a href="https://docs.djangoproject.com/en/4.1/topics/http/views/">простое определение</a>:</p><blockquote>The view itself contains whatever arbitrary logic is necessary to return response.</blockquote><p>Соответственно, в этом слое можно делать ORM-запросы, если это нужно для ответа. Не обязательно пытаться «прятать» этот код куда-то ещё.</p><p>Часть запросов будут нести бизнес-смысл, то есть они станут частью бизнес-логики. Иногда это будет даже не один запрос в БД, а несколько, или даже пара строк кода между ними.</p><p>По принципу MVC «Fat models, skinny controllers, simple views», большую часть такого кода стоит стараться класть в слой Model. Один из самых недооценённых, на мой взгляд, способов — <a href="https://docs.djangoproject.com/en/4.1/topics/db/managers/">переопределение QuerySet</a>. Этот способ позволяет взять здоровый запрос на 10 вызовов методов ORM и дать ему простое, бизнесовое название.</p><p>Со слоем Services получается «Skinny Models, Skinny Controllers, Fat Services». На мой взгляд, это очевидное отхождение от принципов, которые изначально закладывались в фреймворк.</p><h2>Приложения (app)</h2><p>Сейчас эпоха популярности микросервисов над монолитами. Из-за этого сама архитектура Django-монолита стигматезируется, некоторые мои знакомые даже не пытаются пилить Django-монолиты на приложения. Но ведь «монолит» не равно «свалка»! За ним всё ещё стоит ухаживать.</p><p><b>Приложения </b>— это следующий уровень абстракции в фреймворке, который тоже отвечает за разделение кода на кучки, но уже по другому признаку.</p><p>Можно представить, что MTV разделяет код на «красное», «синее» и «зелёное», а приложения — это разделение на «круглое», «квадратное» и «треугольное». Важное правило в делении логики на приложения — <a href="https://enterprisecraftsmanship.com/posts/cohesion-coupling-difference/">Cohesion and Coupling</a>. Если коротко, это о связности между приложениями: нужно добиваться состояния, когда у вас мало связей между приложениями и много внутри них.</p><p>Вот к этому нужно стремиться:</p><figure><img src="https://media.tproger.ru/uploads/2023/04/b8a91d86-4da4-4fb8-8f8e-bbbc99f3a98f-autoconverted.jpeg" alt="" /><figcaption>Картинку взял из всё той же статьи</figcaption></figure><p>Между «разделёнными группами» связей мало, почти все связи спрятались внутри групп. Кружочки одного цвета — это части кодовой базы, которые сильно связаны друг с другом.</p><p>А вот такого стоит избегать:</p><figure><img src="https://media.tproger.ru/uploads/2023/04/a1b8ddf4-03f8-4ef5-af1e-1b917466dfe2-autoconverted.jpeg" alt="" /></figure><p>В первом случае связи хороши, но связанный код разбросан между «приложениями». Придётся часто заходить в разные приложения, чтобы внести правку в логику «жёлтых кусочков кода», например.</p><p>Справа совсем всё плохо: код разделён на такие маленькие группы, что каждый кусочек кода сам по себе стал «группой». Это называется ‘Destructive decoupling’.</p><p>Чтобы не попасть в одну из ситуаций выше, логику приложения нужно <b>группировать по юзкейсам, а не по сущностям</b>. Часто эти два способа дают одинаковые результаты, но так происходит не всегда.</p><p>Когда вы группируете логику в приложения, в первую очередь смотрите, какая логика постоянно совместно используется. Переводя в идею про Cohesion and Coupling — какие из кружочков связаны большим количеством стрелочек.</p><p>Если смотреть на разделение по приложениям так, то, если потянуть за одну функцию, за ней подтянутся и другие, сильно с ней связанные. Иногда часть связей этих функций будет неожиданной, хотя будет казаться, что складывать их вместе «нелогично».</p><p>В такой ситуации советую задуматься: а почему они оказались связанными? Может, вы неправильно выбрали название для приложения и оно должно включать в себя больше.</p><p><b>Не стесняйтесь создавать новые приложения.</b></p><p>Что делать, если одна модель не помещается ни в одно из приложений? Создайте для неё отдельное!</p><p><i>А что, так можно? В целом ПРИЛОЖЕНИИ будет всего одна жалкая моделька?!</i></p><p><b>Да, можно. Даже нужно.</b></p><p>Лучше создавать приложение сразу, как только стало заметно, что оно понадобится. Ведь когда у вас наберётся много логики, разбросанной по разным приложениям, будет уже поздно.</p><p>Процесс выделения этой логики в новое приложение будет болезненным, и, делая это, вы будете задаваться вопросом: «какой м**ак раскидал эту логику по приложениям?».</p><p>Очевидно, слишком увлекаться тоже не стоит. Иначе столкнётесь с Destructive decoupling. Но и бояться создания приложений не нужно.</p><h2>Файлы</h2><p>Ещё один слой разделения логики по фреймворкам — это файл. Мы касались темы файлов выше, давайте резюмируем уже сказанное:</p><ol><li>Свалка в файлах вредит навигации по проекту.</li><li>Cohesion and Coupling поможет разложить логику по файлам.</li><li>Не стесняйтесь создавать файлы, даже если кода внутри будет мало. Это поможет потом не страдать, выкорчёвывая функции из кода.</li><li>Название файла должно задавать чёткую однозначную границу, говорить о том, какому коду здесь не рады. Иначе свалки не миновать.</li></ol><p>На третьей мысли хочется остановиться подробнее. Страх создания файлов зачастую иррационален. У разработчиков редко бывает страх создания новой функции: если это нужно, вы без проблем вынесете кусок логики в новый, небольшой «контейнер» — функцию.</p><p>С файлами аналогично, это тоже контейнер для кода. Просто функции хранят в себе строчки кода, а файлы хранят в себе функции.</p><p>У ситуации «переборщили с количеством файлов» есть надёжный симптом — читая интересующий код, вы не задерживаетесь в одном файле больше, чем на 30 секунд. Всё время приходится двигаться по файлам туда-сюда. А это и есть нарушение принципа Cohesion and Coupling.</p><h2>Итоги</h2><p>Если во время чтения статьи вы успели потерять основные тезисы, то вот небольшой конспект. ?</p><ol><li>Services – не часть MTV;</li><li>Плохое название файла приведёт к свалке;</li><li>Можно писать запросы в слое View, не бойтесь этой практики;</li><li>Монолит – не повод сваливать всё в кучу;</li><li>Группировать по юзкейсам лучше, чем по сущностям;</li><li>Не стесняйтесь создавать новые файлы и приложения, даже если кода в них мало;</li><li>Разложить логику по файлам поможет Cohesion and Coupling.</li></ol>]]></content:encoded>
    </item>
    <item>
      <title>Как мы перебрали бэкенд, и для 20 миллионов юзеров всё прошло гладко</title>
      <link>https://tproger.ru/articles/kak-my-perebrali-bjekend-prilozheniya-na-20-millionov-yuzerov</link>
      <comments>https://tproger.ru/articles/kak-my-perebrali-bjekend-prilozheniya-na-20-millionov-yuzerov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-my-perebrali-bjekend-prilozheniya-na-20-millionov-yuzerov</guid>
      <description><![CDATA[<p>За два года мы переписали инфраструктуру приложения так, что пользователи не заметили перехода. В статье расскажем, как это удалось.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-my-perebrali-bjekend-prilozheniya-na-20-millionov-yuzerov">Как мы перебрали бэкенд, и для 20 миллионов юзеров всё прошло гладко</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 15 Feb 2023 12:30:48 GMT</pubDate>
      <content:encoded><![CDATA[<p>Дзен — это платформа для создания и просмотра контента. Сотни тысяч авторов публикуют посты, лонгриды, длинные видео и короткие ролики, а умные алгоритмы подстраивают ленту под интересы миллионов пользователей. За два года мы полностью переписали инфраструктуру — и ни юзеры, ни авторы контента не заметили перехода. В статье расскажем, как это удалось.</p><h2>Что было до</h2><p>Раньше Дзен состоял из двух бэкендов — так называемые фронт и рекомендер — и балансеров над ними. Фронт принимал весь пользовательский трафик. Нижний, рекомендер, реализовывал систему выдачи и рекомендаций для пользователей контента (500 миллисекунд в 99-м перцентиле) и хранил в себе сотни миллионов документов.</p><p>Раз в день мы проводили единые большие релизы, куда все продуктовые команды сгружали тикеты. Такие релизы было долго и сложно тестировать, они часто ломались и откатывались обратно. Приходилось и подстраиваться под другие команды – если в соседней фиче находили баг, ваша также откатывалась. Нужно было ждать следующего релиза.</p><p>Такой подход влиял и на скорость работы в целом. Сервисы разного объёма — например, получение экспериментов, рекламы и рекомендательная система — лежали на одном бэкенде.</p><p>При этом интеграции с разными браузерами и мессенджерами и 20 миллионов активных ежедневных пользователей оказывали нагрузку до 150 тысяч RPS на сервера. И Дзен развивался. Появлялись новые типы контента — статьи и видео разных форматов, — и отдельные команды, каждая со своими продуктовыми требованиями и стеком.</p><p>Мы поняли: чтобы поддерживать работу и быстро расти, пора переписывать инфраструктуру.</p><h2>Что нужно было учесть</h2><p>Высокая нагрузка, постоянный поток юзеров и нового контента диктовали требования к платформе и нашей работе.</p><ul><li>Durability. Если пользователь написал и сохранил статью, он должен быть уверен, что через неделю она останется на том же месте. Вне зависимости от того, переписали мы что-то или нет.</li><li>Доступность. У Дзена есть концепция четырёх девяток. За отчётный период мы должны быть доступны 99,99% времени, то есть можем простаивать примерно 52 минуты в год.</li><li>Консистентность внутри данных. Если пользователь написал статью, она должна корректно попасть в ленту канала, карусель с подборками новых статей автора, подписки читателя и так далее. И не должна внезапно пропадать из рекомендаций. То есть данные о новой единице контента, подписке или лайке должны консистентно «разойтись» по сервисам, продуктам и счётчикам.</li><li>Скорость. Во-первых, само приложение, все вкладки и окна должны открываться быстро и плавно. Во-вторых, контент должен выгружаться на платформу как можно быстрее. Для этого мы постоянно:оптимизируем алгоритмы рекомендаций;работаем над новыми классификаторами и скоростью разметки контента;переписываем код и ищем, что можно запускать параллельно.</li><li>Микросервисы. Мы обслуживаем большой RPS, разрабатываем множество фич — от студии блогера до ленты комментариев — разными командами. И поддерживать всё это на монолите сложно.</li></ul><ul><li>Железо. Чтобы поддерживать рекомендательный сервис и обрабатывать сотни миллионов документов за миллисекунды, нужно 80 тысяч ядер.</li><li>Плавность. Все рефакторинги должны были происходить для пользователя бесшовно. Для этого мы выделяли ответственность, ту же геобазу, деплоили её в отдельный сервис и затем переставали хранить эту ответственность в монолите.</li></ul><h2>Что мы сделали с бэкендом приложения</h2><h3>Выделили ответственности внутри монолита</h3><p>Ответственность — это часть монолита, которую можно выделить в отдельный сервис или фичу. Например:</p><ul><li>SSR — набор сервисов, которые рендерят наш сайт;</li><li>Клиентский API, куда приходит пользователь, чтобы посмотреть или выложить контент, и все взаимодействия с бэкендом по протоколу клиент-сервер;</li><li>Система авторизации пользователя;</li><li>Система сбора Big Data о пользователе;</li><li>Сервис пользователя — сервис, который резолвит пользователя в конкретную страну/город/район и так далее;</li><li>Рекомендер;</li><li>Сервис, который реализует A/B-тесты (мы можем проводить тысячи таких одновременно) и фиксирует, какие пользователи в какие эксперименты попадают;</li><li>Сервис рекламы — отвечает за то, какую рекламу и в каких количествах показать пользователю.</li><li>И ещё десятки других ответственностей.</li></ul><p>В итоге мы получили «пирог», в котором каждый слой — это отдельная ответственность. И начали искать способ этот пирог порезать.</p><h3>Распилили монолит по этим ответственностям и выделили отдельные сервисы</h3><p>Сначала мы сделали неправильно: взяли этот «пирог» и поделили на куски, которые вбирают в себя все ответственности. Например, мы выделили функциональность, которая выдаёт на странице каждой публикации контент, похожий на неё. И закинули в эту функциональность и клиентский API, и систему авторизации, и сервис пользователя, и рекомендер.</p><p>В итоге каждая ответственность расползалась по разным сервисам — как это было раньше — чего мы и пытались избежать.</p><p>Затем попробовали делить монолит «по слоям»: вынесли SSR, клиентский API и другие — и получили дерево зависимостей этих ответственностей друг от друга.</p><p>На следующем этапе мы переписали дерево. Начали с «листьев». Например, сервис пользователя не имеет своих зависимостей, а является только зависимостью для других сервисов. Мы вынесли её, переписали на новый стек, создали под неё отдельный сервис и выдали его отдельной команде.</p><p>И затем пошли вниз, постепенно выделяя и переписывая те ответственности, которые зависят только от того, что мы уже вынесли в отдельный сервис. Так, у нас получилось примерно 200 сервисов.</p><p>Часть сервисов — например, авторизацию — пришлось писать «сбоку», и потом постепенно переносить коммиты.</p><p>Чтобы организовать работу, мы прописали полноценный роадмап и указали майлстоуны: сегодня вынесли сервис пользователя, через месяц — авторизацию, ещё через месяц — рекомендер. А также проводили регулярные встречи, где фиксировали, на каком этапе распил монолита, какие сервисы будут к концу недели и так далее.</p><h3>Прописали взаимодействие между сервисами</h3><p>Для этого использовали Service Mesh и его реализацию Apphost, написанную Яндексом. У неё несколько плюсов.</p><ol><li>Apphost включает декларативный конфиг зависимостей между сервисами. То есть у него есть граф зависимостей, который получает request пользователя на входе и выдаёт response на выходе. Между ними — облако выделенных нами сервисов, которые зависят друг от друга. Описание графа находится в конфиге Service Mesh нашего Apphost и описано декларативно.</li></ol><figure><img src="https://media.tproger.ru/uploads/2023/02/Untitled-Copy-3.png" alt="" /></figure><ol><li>Apphost ходит в бэкенд приложения, минуя слой балансеров, которые роутят трафик (тех же сайдкаров с HTTP-прокси, через которые сервисы ходят друг в друга). Это даёт преимущество в latency, скорости и надёжности.</li><li>Форсятся правильные политики отказоустойчивости. И разработчику не нужно указывать, с какими ретраями, с каким таймаутом он будет ходить в другой сервис. Так, исключается каскадное падение системы, и никто не может её задедосить (разве что сам Apphost).</li><li>Появился сервисный трейсинг запроса. Благодаря ему мы видим, в какой момент упало выполнение запроса и что конкретно отвалилось. К тому же в микросервисах без него невозможно расследовать инциденты и дебажить.</li></ol><h2>Что нам это дало</h2><p>Мы перешли на архитектуру Service Mesh, выделили сервисы, отдали их продуктовым командам. Так, любой из 200 сервисов смог релизиться отдельно, может быть выкачен и не заблочится на релиз другого сервиса. Также у нас поддерживается обратная совместимость в протоколе их взаимодействия друг с другом.</p><p>Благодаря такому распилу мы смогли сделать чанковую загрузку сайта (HTTP Chunked Encoding). Поисковая строка, рекомендательная лена подгружаются на главную страницу dzen.ru отдельными чанками, за каждый из которых отвечает свой бэкенд. И пока подгружаются тяжёлые рекомендации, более лёгкие элементы сайта уже доступны пользователю. И отображение в целом происходит быстрее, что положительно сказывается на UX пользователя.</p><p>Поначалу копаться в бэкенде приложения было сложно, потому что, по сути, это — большой пласт легаси. В нём оставались наслоения логики, которые складывались годами, невозможно было выяснить, почему конкретный if делает то, что делает. И чтобы убрать этот if, три строчки кода, мы тратили полтора часа на анализ логов.</p><p>Но чем больше сервисов мы выносили из монолита, чем больше задач решали вне его, тем проще было работать. Потому что в нём остались только legacy-ручки для старых клиентов, старые приложения, которые мы редко используем и редко коммитим.</p><p>К тому же помогала мотивация и желание сделать что-то крутое и полезное из большой и тяжёлой структуры.</p>]]></content:encoded>
    </item>
    <item>
      <title>Деплой монолита на .NET Framework 4.8 и Oracle с помощью Ansible</title>
      <link>https://tproger.ru/articles/deploj-monolita-na-net-framework-4-8-i-oracle-s-pomoshhju-ansible</link>
      <comments>https://tproger.ru/articles/deploj-monolita-na-net-framework-4-8-i-oracle-s-pomoshhju-ansible?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/deploj-monolita-na-net-framework-4-8-i-oracle-s-pomoshhju-ansible</guid>
      <description><![CDATA[<p>Рассказываем, как мы настроили деплой монолитного приложения на Ansible и поделились фишками, статьями и скриптами, которые помогут в работе</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/deploj-monolita-na-net-framework-4-8-i-oracle-s-pomoshhju-ansible">Деплой монолита на .NET Framework 4.8 и Oracle с помощью Ansible</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Для продвинутых]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 30 Dec 2022 12:29:17 GMT</pubDate>
      <content:encoded><![CDATA[<p>В статье расскажу, как настроить деплой монолита на Ansible, как перейти с PowerShell и как ускорить развёртывание приложения и базы данных. А ещё поделюсь полезными фишками, статьями и скриптами, которые помогут в работе.</p><ol><li><a href="https://tproger.ru/#part1">Почему Ansible, а не PowerShell</a></li><li><a href="https://tproger.ru/#part2">Как настроить деплой приложения</a></li><li><a href="https://tproger.ru/#part3">Как деплоить базу данных Oracle</a></li><li><a href="https://tproger.ru/#part4">Деплой бэкенда и фронтенда приложения</a></li><li><a href="https://tproger.ru/#part5">И пара лайфхаков</a></li></ol><h2>Почему Ansible, а не PowerShell</h2><ul><li>Один подход написания плейбуков для Windows и Linux-серверов. То есть у нас есть единая среда автоматизации. И если ты умеешь писать под Linux, то сможешь и под Windows.</li><li>Достаточно декларативный язык. Мы указываем, что хотим получить на выходе, и Ansible своим движком выдаёт результат.</li><li>Ansible позволяет хранить всю инвентаризацию серверов и стендов (а только в нашей системе их порядка десяти, плюс различные настройки приложения для разных стендов).</li><li>Огромное количество модулей для работы с различными фреймворками, программным обеспечением и железом.</li><li>Есть полная поддержка Redhat.</li></ul><p>Тем, у кого планируется большой объём автоматизации на Ansible или других скриптах и требуется комплексное управление всем этим процессом, стоит посмотреть в сторону opensource проектов AWX или Rundeck.</p><h2>Как настроить деплой приложения</h2><h3>Подготовить серверы</h3><p>Сперва открываем сетевые проходы от Linux-агентов в TeamCity до управляемых серверов. Это порты 5985/tcp, 5986/tcp для Windows и 23/tcp в Linux для SSH.</p><p>Затем добавляем учётную запись, под которой мы будем подключаться и управлять конечными серверами в локальную группу Администраторы. Включаем службу WinRM — настройки по умолчанию будет достаточно — и аутентификацию CredSSP (другие виды аутентификации либо сложно настроить в компании, либо вовсе нельзя).</p><p>После остаётся только подготовить инструменты для работы с самим Ansible. Я использую Git, VS Code и набор плагинов: GitLens, Redhat Ansible, Redhat XML и Prettier — Code formatter.</p><h3>Создать репозиторий для деплоя</h3><p>В Bitbucket создаём отдельный репозиторий, где будем хранить всё, что связано с деплоем на Ansible. Это нужно, чтобы отделить скрипты деплоя от репозитория проекта системы, тем самым ограничив права доступа к ним. Доступ должен быть только у DevOps-инженеров и прикладных администраторов. Мало ли кто случайно поправит конфиг-файл для продуктивного сервера — и после деплоя получим неработающее приложение.</p><h3>Подготовить билд конфигурации в TeamCity для запуска Ansible</h3><p>Сначала готовим отдельную build configuration (далее — билд), которая собирает Ansible плейбуки для конкретной задачи. Этот билд подключается в build chain общего процесса деплоя, после того как все артефакты прошли проверки ИБ и загружены в хранилище Nexus.</p><p>Это позволяет параллельно использовать билд Ansible для разных веток проекта.</p><p>Допустим, в проекте требуется создать новый Windows-сервис. Разработчики создают отдельную ветку для этой задачи и разрабатывают сервис. Но нам надо уже готовить деплой для будущего сервиса: установить сервис, настроить файлы конфигурации. Чтобы ничего не ломать в текущем деплое, в Ansible-проекте создаём ветку с аналогичным названием под разработку этой новой фичи и тестируем их параллельно.</p><p>После того как сервис будет готов и протестирован вместе с нашим изменённым деплоем, делаем Pull Request в master-ветку Ansible. И получаем master-деплой, уже скорректированный под новый сервис.</p><h2>Как деплоить базу данных Oracle</h2><ol><li>Разработчики готовят изменения в БД в виде SQL-запросов и помещают в changeset Liquibase, которые применяются в процессе обновления.</li><li>Liquibase артефакты проходят проверку синтаксиса и проверку на безопасность, после чего загружаются в Nexus.</li><li>Ansible в виде плейбука запускает процесс применения миграций.</li><li>После обновления БД, Ansible проверяет логи Liquibase, была ли успешной миграция. Если всё хорошо, он переходит к следующей схеме, если нет — выводит сообщение об ошибке.</li></ol><p>Liquibase можно запускать напрямую из агентов TeamCity, в виде команды liquibase —changelog-file=master.changelog.xml update-sql, но мы поместили его запуск в Ansible, чтобы весь процесс деплоя был в плейбуке и не хранился в билдах TeamCity.</p><p>Зелёным цветом подсвечиваем успешные выполнения задач, а красным то что выполняется с ошибкой.</p><p>Иногда миграция БД завершается ошибкой, чаще всего из-за неверных SQL-скриптов. Такие ошибки передаются специалисту, который занимался их разработкой.</p><h3>Почему Liquibase</h3><p>В нашем проекте, в БД хранятся не только данные, но логика приложения (да-да, так тоже бывает, когда приложению более 10 лет).</p><p>И так как у нас есть множество стендов (Dev/Test/Prod и другие), то нужно контролировать процесс миграции схемы базы данных. Запускать файлы с SQL-скриптами для можно, но процесс требует человеческого контроля. А где человек — там потенциальные ошибки: не доглядел, забыл, не проверил. В итоге через какое то время БД на разных стендах будет отличаться. Поэтому для управления процессом миграции схем мы используем Liquibase. Кроме того, он:</p><ul><li>позволяет хранить все изменения, которые мы вносим в базу данных в виде кода;</li><li>позволяет делать ревью миграции;</li><li>лучше контролирует процесс изменения схемы БД;</li><li>разрешён к использованию внутри компании.</li></ul><h2>Деплой бэкенда и фронтенда приложения</h2><p>Ansible из коробки управляет файлами и фаерволом, создаёт сайты и пулы приложений в IIS, настраивает порты, привязывает SSL-сертификаты и многое другое.</p><p>Это ускоряет создание деплоя. Мы можем взять пустой сервер с ОС Windows, запустить установку фронтенда или бэкенда и Ansible настроит всё за нас:</p><p>проверит папку приложения и SSL-сертификат;</p><ul><li>загрузит файлы приложения из Nexus и скопирует на сервер в нужную папку;</li><li>настроит пул приложений и сайт и привяжет порты и SSL-сертификата;</li><li>настроит XML-файлы конфигурации приложения — об этом подробнее ниже;</li><li>настроит аутентификацию для приложения;</li><li>проверит, что все всё запущено и работает;</li><li>настроит сервис Windows.</li></ul><p>На выходе мы получаем готовый бэкенд, фронтенд или интеграционный сервис. Все настройки приложения хранятся в файлах инвентаризаций  для стендов. А для каждого сервера есть переопределения имен, путей к папкам, сертификатов, конфигурационных параметров и прочее.</p><p>Кстати, мы используем и сокращённую версию деплоя, которая пропускает половину задач: останавливает сервис, обновляет файлы и конфигурацию и запускает сервис в работу. Делается это при выполнения плейбука Ansible с помощью тэгов. Такой подход ускоряет деплой примерно на 30%, используется при разворачивании фич на тестирование.</p><h3>Отдельное внимание стоит обратить на файлы конфигураций</h3><p>В .NET Framework 4.8 и ниже конфигурация приложения хранится в XML-файлах  web.config или app.config (далее — конфиг-файл). Из-за этого возникают проблемы:</p><ul><li>надо заботиться об информационной безопасности — то есть в файлах конфигураций не должно быть паролей, токенов и всего что запрещено регламентами ИБ;</li><li>при автоматизации деплоя, нужно оставить возможность откатиться на предыдущую версию;</li><li>кто-то из сотрудников может случайно изменить конфигурацию при коммите; и приложение может перестать работать</li><li>сложно динамически переопределить некоторые параметры конфигурации для стендов.</li></ul><p>В .NET версий, 5.0 и выше всё чуть проще: можно хранить конфиги внутри проекта с переопределением для конкретных стендов, а также использовать app secrets и dotnet_environment. В другом проекте, мы уже используем именной такой подход.</p><p>Есть два варианта решения.</p><h4>Использовать механизм трансформации конфиг-файлов msbuild</h4><p>Мы делаем несколько конфиг-файлов под разные стенды или серверы. И в момент сборки, указываем в Configuration Manager, для какой среды собираем проект: Test, Dev или Prod. Затем msbuild через схему XDT меняет конфиг и помещает его в папку с остальными артефактами приложения</p><p>У такого подхода есть несколько минусов:</p><ul><li>все настройки и схема изменения хранятся внутри проекта;</li><li>конфиг собирается в момент сборки, хранится в артефактах приложения и уже в готовом виде попадает на стенд в процессе деплоя;</li><li>изменениями конфиг-файлов занимаются разработчики;</li><li>хранение паролей в открытом виде в Git и артефактах.</li></ul><p>Да, есть механизм шифрования секций конфигурационных файлов. Но он не снимает проблемы с хранением в  Git — и достаточно сложный в эксплуатации. Лучше доработать приложение, чтобы оно взаимодействовало с ПО HashiCorp Vault или Cyberark.</p><h4>Шаблонизировать конфиг-файлы в Ansible</h4><p>В Git по умолчанию хранится конфигурационный файл с базовыми настройками. Так, разработчикам удобнее работать на локальных машинах.</p><p>В Ansible хранится файл вида app.config.xdt.j2, в котором хранится шаблонизированная схема XDT для модификации эталонного файла. Звучит странно, но это самый гибкий, безопасный и удобный в эксплуатации способ.</p><p>Первичные затраты времени на подготовку таких xdt.j2-файлов, сводятся к сравнению эталонного конфиг-файла с тем что есть на стенде. С этим помогает утилита FatAntelope, которая сравнивает файлы и готовит XDT-схему для модификации. Схему мы помещаем в Jinja-файл и шаблонизируем конкретные параметры.</p><p>То есть в процессе деплоя приложения мы делаем следующее:</p><ol><li>формируем схему: xdt.j2 -&gt; XDT-схема;</li><li>применяем её к эталонному конфигу: Git config + XDTсхема</li><li>gолучаем .config-файл для стенда</li></ol><p>Минусы:</p><ul><li>тратится время на модификацию xdt.j2-файлов</li></ul><h2>И пара лайфхаков</h2><h3>Ускоряем Ansible</h3><p>Когда мы использовали PowerShell для деплоя приложения, запуск скрипта и обновление файлов занимали 20–30 секунд. Но плейбуки на Ansible занимали по 1–2 минуты. Мы начали искать способы ускорить работу.</p><p>Сперва оптимизировали запуск самого PowerShell. В чём суть? Библиотеки, связанные с PowerShell, лежат не в машинных кодах, а выполняют JIT-компиляцию каждый раз в момент запуска PowerShell. Это тормозит запуск любого скрипта Ansible.</p><p>Чтобы такого не было, достаточно выполнить скрипт, который предлагают сами разработчики Ansible, один раз на всех серверах.</p><p>Так, выполнение плейбуков на Windows ускорится на 20–30%.</p><p>Затем мы выяснили, что в Ansible начиная с версии 2.7 добавлена поддержка протокола PSRP. Это ускоряет работу плейбуков ещё на 30%. Из минусов — протокол не так просто включить в нашем случае, так как нужно скачать несколько пакетов и установить их на агентах в TeamCity, где используется Ansible. Для большинства же, всё просто: pip install pypsrp.</p><p>Третий способ — установить OpenSSH 8-й версии на Windows Server и подключаться к нему по SSН-протоколу, а не WinRM. Это тоже ускорит работу, но фактические замеры мы не делали.</p><h3>Добавляем статистику</h3><p>В Teamcity логи Ansible красиво подсвечены, и в конце есть статистика скорости выполнения плейбука и конкретных задач. Это включается добавлением в корень проект деплоя Ansbile конфига anisible.cfg.</p><h3>Линтинг скриптов Ansible</h3><p>Мы достаточно много времени тратим на разработку деплоя. И хорошо бы перед его запуском проверять общее качество кода, синтаксические ошибки. Для этого используйте линтер Anisble, который установлен на агентах TeamCity.</p><p>Нужно установить триггер в TeamCity на появление нового коммита или Pull Request в проекте деплоя. И после автоматом проверяете код на потенциальные ошибки.</p><h3>Тестируем деплой</h3><p>Обычно стенды Dev/Test/Prod отличаются друг от друга. На Dev стенде всё устанавливается на одном сервере, а в продуктиве всё разнесено по разным серверам и используется балансировка.</p><p>Мы часто меняем и рефакторим деплой приложений. И чтобы быть уверенными, что на всех стендах от отработает корректно, необходимо тестировать его после каждого изменения.</p><p>Самое простое — запускать его на стенды. Но если это не получается сделать, например, они все заняты тестировщиками и разработчиками, можно добавить параметр —check во время запуска плейбука. Тогда плейбук отработает всю логику, но без фактического применения на сервере, и вы убедитесь, что всё работает корректно. Для удобства можно сделать кнопку «Тестовый запуск».</p><p>Важно! Не все модули корректно работают в режиме –check. Если вы сначала загружаете файл артефакта, а потом распаковываете его, даже при тестовом запуске он будет реально искать файл для распаковки. А не найдя его выдаст ошибку. Решается это с помощью параметра check_mode: no.</p><p>На этом всё, если будут вопросы пишите. А если что-то заинтересует, расскажем подробнее.</p>]]></content:encoded>
    </item>
    <item>
      <title>Spark-разработка: реализация параллельно выполняемых заданий</title>
      <link>https://tproger.ru/articles/spark-razrabotka-realizacija-parallelno-vypolnjaemyh-zadanij</link>
      <comments>https://tproger.ru/articles/spark-razrabotka-realizacija-parallelno-vypolnjaemyh-zadanij?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Sergey Sikorskiy]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/spark-razrabotka-realizacija-parallelno-vypolnjaemyh-zadanij</guid>
      <description><![CDATA[<p>Как при помощи Apache Spark выполнять параллельные задания на примере обработки дата-сетов и их слияния в единый набор данных.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/spark-razrabotka-realizacija-parallelno-vypolnjaemyh-zadanij">Spark-разработка: реализация параллельно выполняемых заданий</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Пост пользователя]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 05 Dec 2022 09:09:28 GMT</pubDate>
      <content:encoded><![CDATA[<p>Традиционно разработка приложений на Spark сопряжена с вопросами выделения необходимого ресурса кластера и оптимизации запросов для выполнения поставленной задачи.</p><p>Сергей Сикорский, старший разработчик компании Axenix, рассказывает, как это работает на примере обработки дата-сетов и их слияния в единый набор данных.</p><p>Полный код примеров ниже <a href="https://github.com/skiyDev/spark-threading">выложили на GitHub</a>.</p><h2>Исходные условия</h2><p>Apache Spark — популярный open-source фреймворк для распределённой обработки неструктурированных и слабоструктурированных данных. Он реализован в парадигме in-memory вычислений, то есть обрабатывает данные в оперативной памяти для ускоренного выполнения некоторых классов задач, в частности машинного обучения.</p><p>Фреймворк поддерживает работу на Java, R, Python и Scala.</p><p>Как правило выделение ресурсов кластера, за исключением архитектурных особенностей, отличается также спецификой поставленной задачи.</p><p>Вопрос планирования обработки данных в целях повышения скорости их подготовки часто выносится на уровень инструментов оркестрации. При отсутствии таких инструментов разработчики прибегают к использованию таймеров и прочих встроенных в целевую платформу (Linux Cron) инструментов для планирования запуска заданий, что не меняет сути самого подхода.</p><p>Инструменты оркестрации, такие как Airflow, дают возможность управлять на высоком уровне процессом запуска Spark-решения и отслеживать его выполнение, но не управляют самим процессом на микроуровне.</p><p>Поэтому в материале будет рассмотрен подход к микроуправлению отдельными заданиями на Spark средствами распараллеливания вычислений, применение инструментов для управления процессами, а также технические составляющие, связанные с параллельным запуском заданий (jobs) и его особенностями.</p><p>Изложенное является компиляцией опыта автора в области анализа методов повышения производительности решений на Spark Framework в версиях выше 2.2.0.</p><h2>Постановка задачи и очевидное решение</h2><p>Допустим, у нас есть два набора данных (дата-сета). В первом – содержится информация о фильмах и их жанрах, во втором – оценки пользователей. Путём выполнения действий агрегации, объединений и фильтрации мы хотим получить на выходе соответствие идентификатору фильма, количество жанров для него и среднюю оценку пользователей.</p><p>Результаты промежуточных вычислений должны быть сохранены на диск для дальнейшего использования. В первую очередь решим задачу самым очевидным способом, а именно разработаем программу с последовательной обработкой данных. Исходя из постановки задачи, необходимо выполнить отдельную обработку входящих наборов данных и их слияние.</p><p>Логически решение состоит из следующих последовательно выполняемых функций:</p><ul><li>processGenres – выполняет подготовку данных по жанрам для фильмов;</li><li>processRatings – выполняет вычисление средней пользовательской оценки для фильма;</li><li>processStatistics – выполняет формирование результирующего набора данных.</li></ul><p><i>Код запуска последовательного вычисления</i></p><p>Как видно из приведённого примера, мы получили решение многоэтапной обработки и подготовки данных. Однако очевидно, что оно не эффективно: задание выполняется достаточно продолжительное время ввиду последовательной обработки данных.</p><p>Логика обработки данных инкапуслирована в классе Common. Методы класса Common такие как processGenres и processRatings принимают на вход пути к целевым источникам данных и пути для сохранения результата их преобразования, а также возвращают в качестве результата объект Spark DataFrame результата вычисления.</p><p>Метод ProcessStatistics принимает на вход два DataFrame объекта и путь к результирующему набору данных. А класс ArgProperties представляет собой модель параметров командной строки.</p><p>Решение такой задачи с независимым запуском отдельных заданий дало бы преимущество и ускорило бы процесс обработки данных. Рассмотрим, каким образом можно выполнить задачу в оптимальном ключе.</p><h2>Решение с использованием параллельно выполняемых заданий</h2><p>Spark по умолчанию предоставляет контекст выполнения и логично предположить, что мы могли бы запустить несколько контекстов в рамках нашего вычисления.</p><p>Но исходя из рекомендаций разработчиков Spark делать это крайне не рекомендуется, разве что в тестовых целях. Мы можем создать несколько сессий и попробовать выполнить вычисления в них. В любом случае без использования параллельных потоков сделать это не получится.</p><p>Spark потокобезопасен для запуска нескольких заданий и в работе с несколькими сессиями. Однако в конфигурировании отдельных сессий для каждого из заданий есть смысл только в том случае, если используются разные конфигурации подключения к источникам данных.</p><p>В нашем же случае мы храним данные в одной области, допустим в некотором дисковом пространстве. Тогда будет достаточно создать отдельные задания и выполнить их запуск, например, с помощью механизмов распараллеливания на Python или Scala.</p><p>Приведём фрагмент кода из решения, использующий асинхронный запуск заданий на Scala:</p><p><i>Код запуска решения с асинхронно запускаемыми заданиями</i></p><p>Это решение использует концепцию языка Scala для реализации асинхронных вычислений, концепция реализована с помощью объекта Future. Объект Future инкапсулирует в себе некоторое действие и выполняет его не блокирующим способом, предоставляя интерфейс ожидания и возврата результата.</p><p>В данном случае формируется коллекция из двух Future объектов с последующим ожиданием результата из них в основном потоке выполнения программы. Вызов метода sequence от объекта Future возвращает объект Future инкапсулирующий в себе другие асинхронные действия, в данном случае их два.</p><p>В свою очередь Await.result позволяет явно получить результаты из асинхронной операции с указанием времени ожидания, в данном случае бесконечного.</p><p>Также определяется пул потоков ExecutionContext ограничивающий количество одновременно выполняемых асинхронных операция, в данном случае размерность пула 2.</p><p>Как видно на шкале выполнения, теперь задания запускаются асинхронно (задание 1 и задание 2), что ускоряет процесс обработки данных. Запуск происходит с некоторой задержкой, но выполнение происходит параллельно, что видно на рисунки 4.</p><p>Конечно, можно было бы решить данную задачу и через стандартные объекты Thread/Runnable предоставляемые платформой Java. Но тогда пришлось бы дополнительно описать логику сохранения промежуточных данных во внешнюю коллекцию, что делает код менее лаконичным и более объёмным.</p><p>Очевидный плюс такого решения, кроме повышения скорости работы приложения, заключается в повышенном микроконтроле, когда мы можем отслеживать выполнение заданий в рамках одной сессии, а также более тонко управлять всем процессом обработки данных.</p><p>Таким образом, мы получим параллельное выполнение наших задач на кластере из одного приложения на Spark с минимальными изменениями в коде.</p><p>Однако есть некоторые особенности такого подхода к работе приложения:</p><ol><li>По умолчанию все задачи запускаются в FIFO-очереди. Это значит, что первая задача получит максимум требуемых ресурсов. В таком случае нам необходимо указать очередь как FAIR, что обеспечит нас справедливым распределением ресурсов (подробности описаны в официальной документации по Spark).</li><li>Количество выделяемых ресурсов должно быть достаточно для параллельного выполнения задач на кластере. Мы бы могли выделять их динамически, но в таком случае будут получены издержки по времени на выделение и высвобождение необходимого числа исполнителей на кластере. Потребуется точнее рассчитать суммарно достаточное количество ядер и исполнителей для обработки данных, а также настроить буфер и время ожидания обмена данными в случае высоких нагрузок.</li><li>Несмотря на то что Python поддерживает потоки, разработчики Spark Framework не гарантируют запуск каждого задания в соответствующем потоке JVM и для реализация аналогичной функциональность рекомендуют использовать pyspark.InheritableThread.</li></ol><p>Стоит также отметить, что согласно документации, любые действия (чтение, запись) могут быть запущено параллельно. Также это относится к механизмам кеширования данных (cache/persist) и к механизмам создания контрольных точек данных при пакетной обработке (checkpoint).</p><h2>Вывод</h2><p>Поддержка Framework Spark запуска параллельно выполняемых заданий, даёт дополнительные возможности в микроуправлении обработки данных.</p><p>Это может быть полезно в сложных процессах, где выполняется расчёт множества витрин данных, сохранение и/или кэширование промежуточных результатов, с последующим слиянием и записью результатов вычислений.</p><p>В случае необходимости сохранения атомарности такого решения асинхронный запуск заданий может дать прирост в скорости работы.</p><p>Сама же реализация не является особо трудоёмкой и требует лишь учёта дополнительных накладных расходов на работу приложения.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как стать архитектором ПО в 2023 году</title>
      <link>https://tproger.ru/articles/kak-stat-arhitektorom-po-v-2023-godu</link>
      <comments>https://tproger.ru/articles/kak-stat-arhitektorom-po-v-2023-godu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Виктория Овсянникова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-stat-arhitektorom-po-v-2023-godu</guid>
      <description><![CDATA[<p>Что делает ИТ-архитектор и как им стать? Инструкция по старту в профессии: специализации, ключевые задачи и необходимые знания.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-stat-arhitektorom-po-v-2023-godu">Как стать архитектором ПО в 2023 году</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 24 Nov 2022 14:09:43 GMT</pubDate>
      <content:encoded><![CDATA[<p>IT-архитектор — это проектировщик, который принимает ключевые решения по проекту. В банковской сфере IT-архитектору приходится строить сложные системы, учитывая такие особенности, как работа с большим количеством пользователей и объёмами данных.</p><p>Если вы разработчик или системный аналитик и любите решать стратегические задачи, то эта специальность может стать для вас работой мечты. Вместе с Владимиром Григорьевым, экспертом по архитектуре Газпромбанка, мы выделили области знаний, которые помогут сориентироваться в профессии.</p><ol><li><a href="https://tproger.ru/#1">Специализации и задачи IT-архитектора</a></li><li><a href="https://tproger.ru/#2">С какими областями знаний работает IT-архитектор</a></li><li><a href="https://tproger.ru/#3">Что должен знать архитектор ПО</a></li><li><a href="https://tproger.ru/#4">Софт-скилы ИТ-архитектора</a></li><li><a href="https://tproger.ru/#5">Опыт — это проработка гипотез</a></li></ol><h2>Специализации и задачи IT-архитектора</h2><p>В IT-архитектуре много направлений: enterprise, solution, system, data-архитектура. У каждой специализации свои особенности, которые в том числе зависят и от стека конкретной компании. Поэтому не так просто собрать универсальный пул навыков и технологий, подходящий любому IT-архитектору.</p><p>В больших компаниях задачи диверсифицированы по специалистам.</p><p>Как правило, <b>enterprise-архитектор </b>взаимодействует с бизнесом и проектирует концепцию решения. Он передаёт эти наработки <b>solution-архитектору</b>, который прорабатывает детали, учитывая внешние и внутренние взаимодействия. Также на этом этапе подбирается технический стек, на котором будет произведена реализация. Если ландшафт широкий и систем много, то для выбора техстека лучше выделить отдельную роль: технического или системного архитектора.</p><p>После подготовки детального решения, его необходимо проверить на безопасность. В нашем банке этим занимается отдельное подразделение. Если у <b>архитекторов безопасности</b> нет замечаний, то решение согласовано и может идти дальше. В противном случае мы исправляем замечания и проходим очередной круг согласования, пока вопрос не будет решён.</p><p><b>Технический архитектор</b> заведует артефактами развёртывания решений. Он знает, какое ПО использовать для быстрой записи огромных объёмов ненормализованных данных и как их качественно нормализовать. Как правило, для этого он пользуется целыми наборами типовых шаблонных развёртываний разнообразных систем.</p><p>К техническому архитектору могут обращаться на разных стадиях разработки решения: как на этапе создания концепции, чтобы получить подтверждение о возможностях технического продукта, так и на стадиях детального проектирования или согласования с архитекторами безопасности.</p><p>Что почитать: блог Мартина Фаулера про архитектуру ПО</p><p>В компаниях поменьше у IT-архитектора спектр задач может быть шире. Например, не только построить проект, но ещё и провести код-ревью. Тогда у IT-архитектора обязательно должен быть бэкграунд кодера.</p><h2>С какими областями знаний работает IT-архитектор</h2><p>Чтобы грамотно выстраивать проект, ИТ-архитектор в банке должен понимать, как устроены эти области знаний:</p><ol><li><b>Реализация бэкенда.</b> Где хранить данные, как к ним обеспечить доступ.</li><li><b>Реализация фронтенда.</b> Работа с клиентом, интернетом, каналами.</li><li><b>Интеграционная архитектура.</b> Возможности интеграции, как обеспечивать взаимодействие между системами, не положив ни одну из них.</li><li><b>Безопасная архитектура.</b> Как правильно обеспечить взаимодействие клиентов, быстро проводить идентификацию, авторизацию клиента.</li><li><b>Использование MDM.</b> Это системы, которые будут хранить мастер-данные по необходимым для проекта сущностям (данные клиентов, продуктов и т. д.).</li><li><b>Реализация справочников.</b> Важно правильно распространять по своим сервисам справочную информацию.</li><li><b>Монолитная, сервисная и микросервисная архитектуры.</b> Как они устроены, в чём разница, плюсы и минусы подходов.</li></ol><h2>Что должен знать архитектор ПО</h2><h3>Архитектурный фреймворк TOGAF</h3><p>Важно понимать, как строится архитектура предприятий. Мы пользуемся фреймворком <a href="https://pubs.opengroup.org/architecture/togaf9-doc/arch/chap03.html">TOGAF</a>: проект прорабатывается с точки зрения бизнес-требований, технологического стека, архитектуры данных. Эта методология позволяет реализовать устойчивую и высокопроизводительную систему.</p><p>Примечание 1Если при выходе из строя n-компонента система продолжает полноценно функционировать, то её можно считать <b>устойчивой</b>. N — величина динамическая и может изменяться в зависимости от критичности систем.</p><p>Примечание 2Если при увеличении нагрузки система так же качественно отрабатывает запросы, то её можно считать <b>высокопроизводительной</b>. Для банков этот параметр очень важен, ведь предусмотреть действия клиентов возможно далеко не всегда.</p><h4>Бизнес-требования</h4><p>IT-архитектор должен разработать технический сценарий реализации бизнес-задачи.</p><p>Для этого нужно проанализировать требования, которые выдвигает бизнес. Обычно они уже согласованы с другими отделами: безопасниками, проектными менеджерами, другими командами корпоративной архитектуры.</p><p>В требованиях формат задачи описывают без конкретных деталей. Например, бизнесу нужно реализовать возможность максимально быстро сообщать всем системам об изменении GUID клиента. Тогда можно наследовать большое количество фич, которые клиент сделал со старым GUID. Архитектор понимает, что для этого необходимо построить обратный поток из мастер-системы по данным клиента. Очень важно, чтобы на эти изменения подписались все сервисы, задействованные в этой задаче.</p><figure><img src="https://media.tproger.ru/uploads/2022/11/image1-2.png" alt="" /><figcaption>Так выглядит BPMN-модель события на изменение клиентских данных. Это также пример event-driven архитектуры, о которой пойдёт речь ниже.</figcaption></figure><p>Важный момент, IT-архитектор также должен проанализировать и определить <i>потенциальные изменения</i>, чтобы при интеграции новой системы с другими системами не произошло сбоя. Нужно уточнять у бизнеса: «Если изменятся, например, фамилия клиента или поле в паспорте, это критично?». Если да, то всё заносится в бизнес-требования.</p><h4>Техническая проработка</h4><p>После того как все бизнес-требования утверждены, IT-архитектор начинает проектировать схемы, чтобы «посадить» задачу на стек проекта.</p><p>ИТ-архитектор указывает, какие технологии, фреймворки используются в задаче, на чём написано приложение. Мы в Газпромбанке пишем на Java. Но бывают коробочные решения, которые требуют дополнительного внимания. Например, сервис на REST придётся серьёзно переписывать, чтобы вызывать SOAP.</p><p>Подробнее о Java и необходимых навыках можно почитать в кратком руководстве для начинающих или изучить в базовой дорожной карте языка.</p><p>Архитектор учитывает требования безопасности. По сути, это контроль вызовов: кто вызывает сервис, а кто является вызываемым. Если компрометирующий сервис «А» знает, как вызвать сервис «Б» из защищённой системы, то вся система перестаёт быть защищённой. Тогда нужно прописать, что сервис «Б» должен вызывать сервис «А» из доверенной зоны и возвращать данные по своим каналам.</p><p>Также важно понимать, какие данные передаются в этом потоке: только общедоступная информация или ещё и персональная. Если уровень критичности этих данных высок, то нужно принять дополнительные меры по их сохранности, например, выстраивание DMZ (более защищённых зон) для работы с ними.</p><h4>Архитектура данных</h4><p>Архитектор расписывает, как двигаются сущности, например: откуда берутся данные клиента и где прихраниваются, используются ли они только для передачи совместно с другими данными, изменяются ли при передаче, в каком месте сохраняются копии, где мастер-данные.</p><h3>Базы данных</h3><p>В начале 2000–х были две–три основных базы данных: Oracle, MySQL. Сейчас появляется множество новых реализаций: колоночные, документоориентированные, key-value БД. Их назначение примерно одинаковое — они хранят данные, но подходы к записи, чтению, масштабированию у них абсолютно разные.</p><p>Нужно понимать, какие существуют базы данных и какая между ними разница, что они позволяют делать, почему при использовании реляционных БД не всегда можно достичь большего профита. Например, если нет потребности в ACID (Atomicity, Consistency, Isolation, Durability), то можно рассмотреть NOSQL БД. Это даст возможность записывать большие объёмы данных в БД, к тому же масштабировать хранилище будет проще, чем при использовании реляционной БД.</p><p>Что почитать по теме:Как разобраться в видах СУБД<br />NoSQL базы данных<br />Популярные реляционные базы данных</p><h3>Интеграция</h3><p>Раньше использовались двухзвенные архитектуры систем: приложения напрямую интегрировались с хранилищем, и проблем с получением данных не было. Потом стало необходимо шарить данные в другие системы. В этот момент стало ясно, что постоянные запросы в монолиты с лёгкостью могут положить хранилище.</p><p>Сейчас, когда количество данных у банков многократно увеличивается, приходится выстраивать более корректное взаимодействие. Для этого используют интеграционные паттерны: как на бэкенд-систему положить сервисный мидл слой, чтобы он оркестрировал все запросы к этой бэкенд-системе, выступал своего рода буфером и не давал делать 100 вызовов в секунду.</p><p>Кого почитать: Максим Смирнов о сценариях интеграции приложений</p><h3>Паттерны проектирования</h3><h4>Event-driven архитектура</h4><p>Это удобный паттерн с большими функциональными возможностями: одно и то же событие могут принимать сразу несколько сервисов, по-разному на них реагируя. Здесь нет сильной связности между поставщиком и потребителем, они существуют автономно. Как маршрутизатор событий тут актуален Kafka.</p><p>Материалы по теме:<br />Что такое EDA и как для её построения использовать KafkaЧто такое Kafka и как начать с ним работу</p><h4>Domain-driven design (DDD)</h4><p>В этом случае все данные делятся на домены. Здесь нужно на ранней стадии спроектировать систему так, чтобы не допустить протекания доменов.</p><p>Подходы domain-driven design очень актуальны в больших компаниях. Если заранее не «нарезать» предметную область и не определить основные сущности этих доменов, то со временем данные могут задублироваться в разных частях системы, и синхронная актуализация этих данных вызовет трудности.</p><p>Например, если продукты клиента будут храниться не только в бэкенде, но и на уровне сервисов в нескольких местах, то при изменении списка этих продуктов не все зависимые сервисы смогут сразу получить эти данные (если заранее не реализовать обратный поток с этими данными, как описано в примере выше). Тогда в разных каналах у клиента может быть разная информация по продуктам.</p><p>GitHub про DDD: книги, видео, комьюнити, обучающие курсы.</p><h4>Onion-архитектура</h4><p>При проектировании системы за основу берётся независимый Core-уровень, на который накладывается второй, уже зависимый от первого. На второй уровень накладывается третий, который зависит от второго и так далее. При таком подходе верхний UI-слой не сможет управлять Core-уровнем или ходить напрямую в бэкенд, обходя сервисный слой.</p><h4>Подход CQRS</h4><p>CQRS (Command and Query Responsibility Segregation) — это парадигма, при которой операции записи отделены от операций чтения. Она необходима, когда нагрузка на чтение и запись данных распределена несимметрично. Например, когда большая часть бизнес-логики и сложных проверок приходятся на write-систему, а чтение данных происходит чаще, чем вставка изменений.</p><p>Также почитайте о плюсах и минусах паттерна CQRS.</p><h3>Инструменты визуализации решений</h3><ul><li>Бизнес-вижн прописывается с помощью <a href="https://tproger.ru/articles/modelirovanie-biznes-processov-praktika-ispolzovanija-camunda-bpm-v-java-razrabotke/">BPMN-подходов</a> моделирования.</li><li>Для построения архитектурных схем можно использовать <a href="https://support.microsoft.com/ru-ru/office/%D1%81%D1%85%D0%B5%D0%BC%D1%8B-uml-%D0%B2-visio-ca4e3ae9-d413-4c94-8a7a-38dac30cbed6">UML</a> или <a href="https://archimate.visual-paradigm.com/">Archimate нотации</a>. Мы используем Visio, либо аналог Visio (<a href="https://app.diagrams.net/">draw.io</a>).</li></ul><figure><img src="https://media.tproger.ru/uploads/2022/11/image3-1.png" alt="" /><figcaption>Описание движения сущности в информационной архитектуре.</figcaption></figure><figure><img src="https://media.tproger.ru/uploads/2022/11/image7.png" alt="" /></figure><p>Архитектура приложений описывает направление вызова: кто и кого «дёргает». Также на этом уровне отмечаются технические особенности решения и протоколы, по которым необходимо организовать взаимодействие.</p><ul><li>Существует также «салфеточный дизайн»: когда архитектура рисуется от руки, ИТ-архитектор ставит свою подпись и отдаёт в реализацию. В небольших компаниях такого вполне достаточно. Но в крупном бизнесе, когда потребителей слишком много, нужно согласовывать все решения с энтерпрайз-архитектурой и отделом безопасности.</li></ul><h2>Софт-скилы ИТ-архитектора</h2><p>IT-архитектор выступает посредником между бизнесом и техническими специалистами. Поэтому важный софт-скил архитектора — <b>быть переговорщиком</b>. Он должен договариваться, находить компромиссы, аргументировано объяснять бизнесу технические сложности реализации проекта, чтобы ставить адекватные сроки выполнения.</p><p>При этом также важны умение работать в команде и лидерские качества. Нужно доносить важность проекта до разработчиков: уметь мотивировать, находить к ним подходы, объяснять важность задач и правильно ставить их приоритетность.</p><p>Нужно развивать критическое мышление. В этом помогает <b>концепция</b> <b>helicopter view</b>: быть больше своих задач, смотреть выше. Это даёт более объёмную картину устройства проекта. Нужно анализировать, какой у задач бизнес-эффект, какой эффект будет при интеграции с другими сервисами продукта. Чем выше ты смотришь, тем больше вопросов себе задаёшь: действительно ли здесь нужно сделать именно так, или можно по-другому.</p><h2>Опыт — это проработка гипотез</h2><p>В профессии ИТ-архитектора всё очень индивидуально и зависит от решений, которых придерживаются в конкретной компании.</p><p>Так как же стать IT-архитектором? Выучить пару фреймворков и инструментов визуализации недостаточно. Сейчас в профессию приходят специалисты с уверенными хард-скиллами — системные аналитики, разработчики или другие крепкие айтишники.</p><p>Начинающему архитектору ПО нужно обязательно проверять свои гипотезы на встречах, группах, стратсессиях. Ведь профессионализм и компетенции в этой сфере нарабатываются не столько теоретическими знаниями, сколько опытом и решением практических задач.</p>]]></content:encoded>
    </item>
    <item>
      <title>Кто такой архитектор, и как им стать</title>
      <link>https://tproger.ru/articles/kto-takoj-arhitektor-i-kak-im-stat</link>
      <comments>https://tproger.ru/articles/kto-takoj-arhitektor-i-kak-im-stat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kto-takoj-arhitektor-i-kak-im-stat</guid>
      <description><![CDATA[<p>Рассказываем, чем занимается ИТ-архитектор и какие навыки ему требуются, как войти в профессию и можно ли создать матрицу.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kto-takoj-arhitektor-i-kak-im-stat">Кто такой архитектор, и как им стать</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Карьера]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 09 Sep 2022 15:26:55 GMT</pubDate>
      <content:encoded><![CDATA[<p>Архитектор ПО — важнейший специалист для создания крупных систем. Это гуру разработки и сетевой инфраструктуры, мастер предугадывать возможные проблемы и немого техномаг. Lead architect Группы «Иннотех» Александр Соляр поделился, чем занимается ИТ-архитектор, как войти в профессию и можно ли создать матрицу.</p><h2>Кто такой архитектор</h2><p>Очень просто понять на аналогии. Представьте поезд с большим количеством вагонов (больше сотни), который движется на высокой скорости. А ещё в нём периодически загораются вагоны. Так вот, архитектор — это человек с огнетушителем в руках, который знает, как всё внутри поезда устроено.</p><p>Архитектор может точечно взять на себя ответственность тушить пожар там, где это необходимо в первую очередь, чтобы поезд продолжал двигаться. При этом специалист одновременно будет осуществлять взаимодействие и координацию с другими гигантскими поездами, которые несутся параллельно или навстречу ему, чтобы аварии не произошло.</p><p>Я, как архитектор, просто кайфую от создания сложных систем с нуля и их развития. Мне очень нравится планировать будущее масштабирование, гибкость и возможность расширения проектов. На самом деле это завораживает.</p><h2>Какие знания требуются архитектору</h2><p>Архитектору в принципе надо уметь абстрагироваться от некоторых вещей и развивать гибкость мышления, чтобы смотреть на проект под разными углами, суметь его прокручивать, разбирать на логические части и находить наиболее подходящие решения под ту или иную ситуации.</p><p>Важна высокая обучаемость, потому что во время работы тебе приходится постоянно разбираться в новых системах, взаимодействиях, стеках, с которыми ранее не сталкивался. Нужно впитывать знания в очень короткие сроки, особенно когда пытаешься какие-нибудь передовые штуки использовать на проектах.</p><p>Всем архитекторам присуща высокая стрессоустойчивость. Это позволяет качественно находить альтернативы и консенсус даже в экстремальных ситуациях. Например, при необходимости оперативного импортозамещения.</p><p>Не стоит забывать об умении находить общий язык с бизнесом. Приходится часто общаться с заказчиками и доносить те или иные архитектурные решения с учётом бизнес-потребностей. Архитектор в этом случае выступает в качестве партнёра, который указывает на потенциальные проблемы и моменты, требующие заблаговременного решения.</p><p>Крайне желателен опыт создания систем с нуля. Это учит видеть картину целиком, не ограничиваясь прикладной интеграцией или базой данных. А также оценивать проект с точки зрения тех, кто будет на нём работать, обслуживать его и совершенствовать в дальнейшем. И архитектор как раз должен выступать точкой баланса в этом отношении.</p><p>Также необходимо выделять отдельно любовь к людям, заботу о системе и долгосрочных взаимодействиях. Архитектура — это не про здесь и сейчас что-то сделать. Упор на строительство фундамента, на основе которого и будут строиться дальнейшие системы.</p><p>Лишних знаний у архитекторов в принципе никогда не бывает. Потому важно расширять постоянно кругозор.</p><h2>С каких специальностей чаще всего приходят в архитекторы</h2><p>В архитекторы можно прийти из разработки или системного анализа. Но в последнем случае крайне желательно освоить программирование, чтобы разговаривать на одном языке с разработчиками.</p><p>Если человек очень хочет пойти в архитектуру, есть только один очень эффективный способ понять — его ли это вообще. Но к сожалению, он не для всех. Нужно прийти к руководителю и озвучить желание заниматься архитектурой. Внутренне согласиться, что полгода дополнительно к основной работе безвозмездно исполнять обязанности архитектора на каком-то другом проекте под руководством опытного куратора. Выкинуть орла из гнезда — если полетит, то через полгода адаптируется.</p><p>Можно также создать какой-то пет-проект с нуля. Просто нарисовать, как он работает, распределить базы данных, что откуда берётся, как всё это осуществляется, бизнес-процесс наложить на инфраструктуру, желательно микросервисную, чтобы система хотя бы на бумаге работала.</p><p>Дешёвого рецепта быстро, просто и безболезненно стать архитектором нет. Необходимо годами нарабатывать опыт и развивать знания. Но даже это не гарантирует того, что человек, который окунётся целиком в корпоративную и солюшн архитектуру, сможет эффективно справляться с обязанностями.</p><h2>Какая перспектива у архитектуры</h2><p>Ранее архитекторы занимались только серверной инфраструктурой. Но сейчас все технологии двигаются в облака. Наша компания для этого делает очень много, развивая самые передовые технологические стеки. Мы разрабатываем решения как раз на тех стеках, которые позволят нам в будущем конкурировать и на международной арене.</p><h2>Какие книги можно посоветовать начинающим архитекторам</h2><p>Могу посоветовать только английские книги, которые наиболее актуальны для современных архитекторов:</p><ul><li>Release It!. Design and Deploy production-ready software, Michael T.Nygard;</li><li>Design It! From programmer to software architect, Michael Keeling;</li><li>Software Architecture in Practice, Len Bass, Paul Clements, Rick Kazman;</li><li>37 Things One Architect Knows About IT Transformation: A Chief Architect’s Journey, Gregor Hohpe;</li><li>Learning Domain-Driven Design, Vlad Khononov.</li></ul><h2>Способен ли архитектор создать Матрицу из одноимённого фильма</h2><p>IT-архитектор — это человек, который как минимум может эту систему спроектировать. А как максимум создать.</p><p>У меня был кейс, когда мне пришлось на коленке написать большую систему для энтерпрайза. Заказчики говорили, что эту систему нужно разрабатывать три с половиной года. Я за несколько месяцев сделал документацию на 250 страниц с макетами. Да, они были далеко не с самым приятным UI/UX дизайном, но функциональные. Прописал всю логику, алгоритмы, реализовал их не на самом передовом стеке из подручных средств, но это работало.</p><p>В течение недели два департамента согласовали документацию. Ни у кого не было вопросов и контраргументов из серии «эту систему поднять нереально», «она будет разрабатываться усилиями разработчиков несколько лет» и так далее. Но перенос на плечи архитектора разработку кода — крайний метод, который никогда лучше не использовать, потому что он, как правило, трудозатратный.</p><p>Архитектор — это тот человек, который иногда спускается со своей башни из слоновой кости, чтобы подсказывать коллегам и развивать их. Это помогает членам команды становиться лучше и расширять горизонты.</p><p>Эксперты «Иннотех» — именно те ребята, которые активно делятся знаниями друг с другом и с командами, чтобы они работали более эффективно и продуктивно. Физических возможностей архитектора не хватит для того, чтобы он везде закрывал проблемные места, поэтому обмен информацией так важен. Ресурсы таких специалистов использовать надо крайне экономно и давать им передышку, потому что объём очень работы и знаний большой, иногда слишком.</p>]]></content:encoded>
    </item>
    <item>
      <title>Топ-5 архитектурных паттернов для распределённых систем</title>
      <link>https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem</link>
      <comments>https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Борисенков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem</guid>
      <description><![CDATA[<p>Пять архитектур распределённых систем с плюсами, минусами и областями применения: такие паттерны помогают крупным веб-приложениям оставаться отзывчивыми.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/top-5-arhitekturnyh-patternov-dlja-raspredeljonnyh-sistem">Топ-5 архитектурных паттернов для распределённых систем</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Jun 2021 13:26:10 GMT</pubDate>
      <content:encoded><![CDATA[<p>Распределённые приложения — главный элемент современной индустрии разработки ПО. Они имеют решающее значение для облачных сервисов хранения данных и позволяют веб-приложениям с огромной аудиторией оставаться реактивными. Для того чтобы эффективно проектировать эти системы, программисты используют фундаментальные блоки — паттерны распределённых систем.</p><p>Мы рассмотрим пять архитектур распределённых систем, их плюсы, минусы и области применения.</p><h2>Что такое паттерн распределённой системы?</h2><p>Паттерны проектирования — это проверенные способы создания систем, каждый из которых подходит для определённого случая. Паттерны абстрактны и не опираются на конкретную реализацию. Большинство паттернов разрабатываются и обновляются множеством разработчиков на протяжении многих лет. Поэтому они зачастую очень эффективны на старте разработки.</p><p>Паттерны — это строительные блоки, которые позволяют программистам использовать существующие знания вместо того, чтобы начинать с нуля строительство каждой системы. Кроме того они имеют набор стандартных моделей, которые помогают другим разработчикам понять, как их проекты могут взаимодействовать с данной системой.</p><p>Паттерны распределённых систем описывают взаимодействие различных узлов системы и то, как они обрабатывают задачи и виды процессов для различных задач.</p><p>Эти паттерны широко используются в архитектуре крупномасштабных облачных вычислений и систем масштабируемых микросервисов.</p><h3>Типы паттернов распределённых систем</h3><p>Большинство паттернов распределённых систем попадает в одну из трёх категорий, в зависимости от функциональности с которой они работают:</p><ul><li>Взаимодействие объектов: описывают протоколы отправки сообщений и разрешения для общения между компонентами системы.</li><li>Безопасность: обеспечивают конфиденциальность, целостность и доступность для защиты системы от несанкционированного доступа.</li><li>Событийные: описывают создание, определение, использование и реакцию на события системы.</li></ul><h2>1. Разделение ответственности на команды и запросы (CQRS)</h2><p>Этот паттерн предполагает разделение операций чтения и записи для увеличения масштабируемости и безопасности системы. Он использует команды для записи данных в постоянное хранилище (они ничего не возвращают) и запросы для обнаружения и получения данных (не могут изменять данные).</p><p>Команды и запросы обрабатываются центром управления, который получает запросы от пользователей. Затем центр получает данные и выполняет необходимые изменения, сохраняет их и уведомляет сервис выполняющий чтение. Этот сервис обновляет модель чтения и показывает изменения пользователю.</p><p>Плюсы:</p><ul><li>Уменьшение сложности системы благодаря делегированию.</li><li>Обеспечение чёткого разделения между бизнес-логикой и валидацией.</li><li>Разделение ответственности.</li><li>Уменьшение количества непредсказуемых изменений распределённых данных.</li><li>Уменьшение числа сущностей, которые могут изменять данные.</li></ul><p>Минусы:</p><ul><li>Требует постоянного взаимодействия между командами и запросами.</li><li>Может увеличить задержку при отправке большого количества запросов.</li><li>Нет средств связи между сервисными процессами.</li></ul><h3>Область применения</h3><p>CQRS отлично подходит для приложений, интенсивно использующих данные, например систем управления базами данных SQL или NoSQL. Также паттерн используется для архитектур микросервисов с большим объемом данных. Он прекрасно подходит для приложений, сохраняющих состояние благодаря разделению на писателя и читателя.</p><h2>2. Двухфазная фиксация (2PC)</h2><p>2PC похож на CQRS использованием транзакций и центра управления, но здесь разделение производится на основании того, на какой стадии находится транзакция. Есть две фазы: фаза подготовки (в которой центр управления сообщает службам подготовить данные) и фаза фиксации (которая сигнализирует службе отправить подготовленные данные).</p><p>Все сервисы в 2PC по умолчанию заблокированы и не могут отправлять данные. После завершения подготовки координатор по одному разблокирует сервисы и запрашивает их данные. Если сервис не готов подтвердить данные, координатор переходит к другому сервису. Когда все подготовленные данные отправлены сервисы остаются разблокированными и ожидают задачи от координатора.</p><p>2PC гарантирует, что одновременно может работать только одна служба, что делает процесс более устойчивым и последовательным, чем CQRS.</p><p>Плюсы:</p><ul><li>Консистентность и устойчивость к ошибкам из-за отсутствия параллельных запросов.</li><li>Масштабируемость — может обрабатывать большие пулы данных так же эффективно, как и данные с одного компьютера.</li><li>Поддерживает одновременную изоляцию и разделение данных.</li></ul><p>Минусы:</p><ul><li>Не отказоустойчив, подвержен возникновению узких мест и блокировок из-за своей синхронной природы.</li><li>Требует больше ресурсов, чем другие паттерны.</li></ul><h3>Область применения</h3><p>2PC лучше всего подходит для распределенных систем, имеющих дело с транзакциями, которые отдают предпочтение точности, а не эффективности использования ресурсов. Он устойчив к ошибкам и позволяет легко отследить ошибки даже в больших объёмах информации.</p><h2>3. Saga</h2><p>Saga — это асинхронный паттерн не использующий центр управления. Сервисы здесь сами взаимодействуют между собой. Эта особенность позволяет избавиться от недостатков упомянутых выше паттернов.</p><p>Для связи между сервисами в Saga используется шина событий. Шина передаёт запросы между службами, и каждая участвующая служба создает локальную транзакцию. Затем участвующие службы выдают событие для получения другими службами. Все другие службы прослушивают события. Первая служба, получившая событие, выполнит необходимое действие. Если этой службе не удается выполнить действие, оно отправляется в другие службы.</p><p>Эта структура похожа на 2PC тем, что службы циклически запускаются, если кто-то не может выполнить задачу. Тем не менее, Saga не использует центр управления, чтобы лучше управлять потоком и уменьшить количество требуемой обратной связи.</p><p>Плюсы:</p><ul><li>Отдельные сервисы могут обрабатывать более долгие транзакции.</li><li>Децентрализация отлично подходит для распределённых систем.</li><li>Меньше узких мест благодаря одноранговой связи между службами.</li></ul><p>Минусы:</p><ul><li>Асинхронная независимость делает трудным отслеживание сервисов выполняющих индивидуальные задачи.</li><li>Сложный механизм управления затрудняет отладку.</li><li>Меньшая изоляция сервисов, чем в предыдущих паттернах.</li></ul><h3>Область применения</h3><p>Этот паттерн распределённой системы хорошо подходит для задач, которым требуется масштабируемая беcсерверная архитектура, способная обрабатывать много запросов одновременно. AWS использует подобные решения в <a href="https://aws.amazon.com/ru/step-functions/?step-functions.sort-by=item.additionalFields.postDateTime&amp;step-functions.sort-order=desc">AWS Step Functions</a> и <a href="https://aws.amazon.com/ru/lambda/">AWS Lambda</a>, и других продуктах.</p><h2>4. Реплицированные сервисы с распределением нагрузки (RLBS)</h2><p>RLBS — это самый простой и часто используемый шаблон проектирования. На базовом уровне он состоит из нескольких идентичных сервисов, которые общаются с центральным распределителем. Каждый сервис способен выполнять задачи и перезапускать их в случае неудачи. Распределитель получает запросы от конечного пользователя и разделяет их между сервисами, используя round-robin или более сложный алгоритм.</p><p>Дублирующие службы обеспечивают высокую доступность приложения для запросов пользователей и могут перераспределять работу в случае сбоя одного экземпляра службы.</p><p>RLBS часто используется с Azure Kubernetes, которая представляет собой технологию оркестровки контейнеров с открытым исходным кодом, разработанную Microsoft, которая предлагает автоматическое масштабирование служб в зависимости от воркфлоу.</p><p>Плюсы:</p><ul><li>Стабильная производительность с точки зрения конечного пользователя.</li><li>Быстрое восстановление после сбоев.</li><li>Высокая масштабируемость через увеличение числа сервисов.</li><li>Отличная многопоточность.</li></ul><p>Минусы:</p><ul><li>Нестабильная производительность, зависящая от алгоритма балансировки.</li><li>Управление сервисами требует много ресурсов.</li></ul><h3>Область применения</h3><p>RLBS отлично подходит для систем, с которыми пользователи взаимодействуют напрямую. Нагрузка на эти системы в течение дня изменяется, поэтому требуется низкая задержка, например Netflix или Amazon Prime.</p><h2>5. Шардинг</h2><p>Альтернативой репликации является создание отдельных сервисов, каждый из которых выполняет определённый тип запросов. Это называется шардингом, потому что вы разделяете запрос на несколько неодинаковых частей. Например, у вас может быть отдельный сервис, который принимает кэширующиеся запросы, а другой сервис будет отвечать за запросы с высоким приоритетом. Распределитель нагрузки обрабатывает каждый запрос и передаёт его в подходящий сервис.</p><p>Шардинг сервисов обычно используется для создания сервисов с поддержкой состояния, потому что объём состояния часто слишком большой для одного stateless контейнера. Шардинг позволяет масштабировать отдельные элементы под размер состояния.</p><p>Шардинг также позволяет быстрее обрабатывать высокоприоритетные запросы. Сегменты, предназначенные для запросов с высоким приоритетом, всегда доступны для обработки таких запросов в момент их поступления и не требуют размещения запроса в очереди.</p><p>Плюсы:</p><ul><li>Возможность масштабировать сегменты для общих запросов.</li><li>Легкая приоритизация запросов.</li><li>Простая отладка, благодаря естественной сортировке.</li></ul><p>Минусы:</p><ul><li>Поддержка большого числа сегментов может потребовать много ресурсов.</li><li>Непропорциональное использование сегментов может привести к потерям производительности.</li></ul><h3>Область применения</h3><p>Шардинг сервисов хорош в тех случаях, когда ваша система работает с предсказуемой несбалансированной нагрузкой для разных запросов, а некоторые запросы имеют приоритет.</p><h2>Что дальше?</h2><p>В статье были рассмотрены лишь несколько паттернов распределённых систем. Вот ещё несколько шаблонов проектирования для изучения:</p><ul><li>Sidecar паттерн;</li><li>Упреждающая журнализация (Write-Ahead Log);</li><li>Split-Brain паттерн;</li><li>Направленная отправка (Hinted Handoff);</li><li>Чтение с восстановлением (Read Repair).</li></ul><p>Мы спросили экспертов о том, <a href="https://tproger.ru/experts/which-design-patterns-to-learn/">какие шаблоны проектирования стоит знать каждому</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Микрофронтенды: зачем нужны и как к ним прийти</title>
      <link>https://tproger.ru/articles/mikrofrontendy-zachem-nuzhny-i-kak-k-nim-prijti</link>
      <comments>https://tproger.ru/articles/mikrofrontendy-zachem-nuzhny-i-kak-k-nim-prijti?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/mikrofrontendy-zachem-nuzhny-i-kak-k-nim-prijti</guid>
      <description><![CDATA[<p>Микрофронтенды делят монолитное приложение на изолированные части, которые команды могут независимо разрабатывать, тестировать и развёртывать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/mikrofrontendy-zachem-nuzhny-i-kak-k-nim-prijti">Микрофронтенды: зачем нужны и как к ним прийти</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 03 Mar 2021 15:53:28 GMT</pubDate>
      <content:encoded><![CDATA[<p>Микрофронтенды — достаточно хайповая тема во фронтенд-разработке. Если коротко, то это деление монолитного приложения на семантически изолированные части и их независимая разработка. Каждая страница сайта или приложения структурно состоит из блоков, это может быть блок рекламы, блок обратной связи и так далее. Они взаимодействуют между собой по задуманной разработчиком логике, но при этом их можно разрабатывать, тестировать и деплоить как отдельные приложения.</p><p>Сама идея не нова и пришла из мира бэкенда — речь о микросервисной архитектуре. Суть та же: у нас есть большое приложение, которое нужно продолжать поддерживать и разрабатывать. Над этим трудятся несколько команд, каждая из которых занимается своей частью. В логике монолитного бэкенда разрабатывать свою часть без риска затронуть функциональность другой части приложения непросто. Поэтому бэкенд разделяет приложение на микросервисы и разрабатывает их изолированно, но при этом так, чтобы они продолжали взаимодействовать между собой.</p><p>Микрофронтенды, как и микросервисная архитектура, — результат естественного эволюционного процесса. Старый подход в виде монолита был удобен во многих смыслах, но с учетом усложнения технологий и роста приложений приобрел свои минусы. Монолит плохо масштабируется, он труден в поддержке, разработке и тестировании.</p><p>Классический способ борьбы со сложностями — это разделение на составляющие. Если вести разработку всего приложения целиком сложно, нужно «распилить» его на части и разрабатывать отдельно.</p><h2>Где подвох</h2><p>Логика микросервисов наложилась на фронтенд-разработку. Программистам, работающим на «фронт», тоже хотелось приобрести гибкость и независимость команд, ту масштабируемость, которая характерна для микросервисной архитектуры.</p><p>Одно но: фронтенд всегда работает с единой средой, которая трудно отделима. Он выполняется на стороне браузера (клиента), поэтому в рамках одного приложения всегда есть только одна адресная строка, один глобальный объект localStorage, один DOM. Как бы мы ни делили приложение, некая общая часть всегда остается. Именно вокруг этой проблемы строятся основные ограничения микрофронтендов.</p><p>И главное разочарование: в рамках микрофронтенда не получится организовать такую же изолированность частей приложения, как при микросервисной разработке в бэкенде. Во всяком случае пока.</p><p>Так как полной изолированности не достичь, программистам фронтенда все равно приходится очень взвешенно принимать любые изменения в коде. Поэтому при разработке приложения в логике микрофронтендов очень важна экспертиза разработчиков. Они должны понимать, как потенциально может повлиять на приложение в комплексе то или иное изменение в коде.</p><h2>Если вы хотите перейти на микрофронтенд</h2><p>В первую очередь, поймите, зачем это вам. Микрофронтенд нужен не всем. Да, это тренд, который сейчас активно обсуждается в комьюнити. Да, многие большие компании так или иначе придут к этой архитектуре. Но все это актуально в случае объемного приложения. Если сайт состоит из нескольких страниц, микрофронтенд теряет смысл. Это уже оверхэд. Смысл в делении монолита есть, если у вас крупный проект, несколько команд разработки, которые, возможно, используют разные фреймворки. Это, к слову, еще один плюс микрофронтенда: разные команды могут писать код на разных стеках. Кто-то пишет на React, кто-то — на Angular, но все объединяется в работе одного приложения.</p><p>Затем нужно понять, как именно можно поделить ваш монолит на относительно независимые для разработки блоки. Чаще всего отталкиваются от команд разработки. Обычно деление зон ответственности уже есть, просто оно не воплощено в архитектуре. Например, в <a href="https://selectel.ru/">Selectel</a> основной продукт разработки фронтендеров — это Панель управления, где клиенты могут приобретать и использовать наши услуги. При этом в самой панели также есть деление на продукты компании и, как правило, у каждого продукта, будь то выделенные серверы, «Облачная платформа Selectel», Managed Kubernetes и т.д., есть свои фронтенд-специалисты. То есть они уже развивают только определенную часть панели.</p><p>Выберите инструменты, которые помогут вам построить микрофронтенд-архитектуру. Сейчас появляется все больше библиотек, инструментов, которые помогают разработчикам на пути к микрофронтендам. Например, такая библиотека, как single-spa, которая объединяет несколько javascript-приложений в одно. Или спецификация Realms, которая позволит на уровне нативного javascript упростить разработку микроcервисов на фронтенде.</p><p>Современные фреймворки типа Vue и Angular также имеют некоторые возможности, позволяющие писать свое приложение так, чтобы в результате его проще было интегрировать в микрофронтенды.</p><h2>Опыт компаний</h2><p>В целом, большинство крупных компаний так или иначе перейдут на микрофронтенд-архитектуру для своих приложений. Известно, что в России так работают в «Додо-пицце», Avito, «Тинькофф банке».</p><p>Selectel тоже стремится к этому. К микрофронтендам мы подходим как к неизбежной необходимости. В компании большой продуктовый портфель, включающий как базовые услуги дата-центров, так и современные PaaS. Как я уже писал, развитием продуктов занимаются, как правило, отдельные команды. И уже сейчас мы отмечаем сложности в их синхронизации при разработке и релизах.</p><p>Наш путь к микрофронтендам начался во многом из-за переезда с AngularJS на Angular нашей Панели управления. Сейчас мы в процессе миграции и поддерживаем гибридную структуру фронтенда.</p><p>Как известно, есть несколько способов миграции из одного фреймворка в другой. Первый — более дорогой и долгий — подразумевает поддержание приложения на старом коде и параллельное его переписывание.</p><p>Второй способ, который мы в итоге выбрали, — более мягкий. Когда мы внутри нашего текущего приложения подключаем другой фреймворк. И потихоньку, кусок за куском, переписываем код приложения. И старая, и новая версии работают одновременно. В процессе переписывания мы рефакторим код. А в течение рефакторинга выделяем части приложения, делаем их более изолированными, избавляясь от лишней связанности. Таким образом, мы создаем подобие микрофронтенд-архитектуры.</p><h2>Как решать проблему общих ресурсов</h2><p>Хорошим шагом к организации микрофронтендов может стать создание монорепозитория. Это git-репозиторий, внутри которого содержатся ваши npm-пакеты, зависящие друг от друга. Монорепозиторий поможет решить проблему с конфликтом версий библиотек и ведением разработки разными командами — все в одном месте, не нужно бегать по разным репозиториям и линковать пакеты.</p><p>При разработке Панели управления Selectel мы как раз используем монорепозиторий. Раньше у нас каждая услуга была отдельным git-репозиторием. В результате возникали сложности не только при локальной разработке, когда нужно было линковать пакеты, но и при обновлении библиотек и сборке в CI. Монорепозиторий решил практически все эти проблемы, причем на его создание ушло не так много времени. С точки зрения рабочих процессов монорепозитории облегчают организацию микрофронтендов.</p><p>Упомяну и очевидные вещи: не стоит исключать лучшие практики разработки (в том числе DRY, KISS, YAGNI) и простую коммуникацию — обмен опытом между фронтенд-разработчиками компании.</p><h2>Выделим ключевое</h2><p>Микрофронтенды — это деление монолитного приложения на семантически изолированные части и их независимая разработка.</p><p>Микрофронтенды имеют смысл, если:</p><ol><li>У вас большое приложение.</li><li>Несколько команд разработки.</li><li>Вы все чаще сталкиваетесь с проблемами при выкате обновлений приложения.</li><li>Преимущества монолита обернулись в минусы.</li></ol><p>Для остальных проектов микрофронтенды будут избыточны.</p><p>Ограничения микрофронтендов:</p><ol><li>Остается проблема общих ресурсов. Полноценная изоляция, как в случае с микросервисной архитектурой в бэкенде, пока невозможна.</li><li>Требуется прослойка для оркестрации приложения, которая может вносить свой оверхэд.</li></ol><p>Что поможет справиться с ограничениями:</p><ol><li>Создание монорепозитория.</li><li>Использование специальных библиотек и инструментов.</li><li>Хорошая команда специалистов.</li><li>Использование лучших практик разработки (например, DRY, KISS, YAGNI).</li></ol>]]></content:encoded>
    </item>
    <item>
      <title>Моделирование бизнес-процессов: практика использования Camunda BPM в Java разработке</title>
      <link>https://tproger.ru/articles/modelirovanie-biznes-processov-praktika-ispolzovanija-camunda-bpm-v-java-razrabotke</link>
      <comments>https://tproger.ru/articles/modelirovanie-biznes-processov-praktika-ispolzovanija-camunda-bpm-v-java-razrabotke?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/modelirovanie-biznes-processov-praktika-ispolzovanija-camunda-bpm-v-java-razrabotke</guid>
      <description><![CDATA[<p>Ведущий Java-разработчик Usetech разбирает компоненты Camunda: развёртывание системы, деплой простого процесса и связку с приложением на SpringBoot.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/modelirovanie-biznes-processov-praktika-ispolzovanija-camunda-bpm-v-java-razrabotke">Моделирование бизнес-процессов: практика использования Camunda BPM в Java разработке</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 25 Jan 2021 07:10:50 GMT</pubDate>
      <content:encoded><![CDATA[<p>Меня зовут Евгений Тришечкин, я ведущий Java разработчик ростовского офиса компании Usetech, и сегодня хочу поделиться с вами применением Camunda BPM в Java разработке.</p><p>В рамках статьи я расскажу об основных компонентах системы управления бизнес-процессами Camunda. Покажу развертывание системы, моделирование и деплой простого процесса. А также, на примере созданного процесса, проиллюстрирую взаимодействие Java/SpringBoot приложения с Camunda.</p><p>Давайте сначала разберемся с терминологией и выясним, что же такое BPM, BPMS и BPMN.</p><p>BPM (Business process management) — концепция управления организацией, рассматривающая бизнес-процессы как особые ресурсы предприятия, непрерывно адаптируемые к постоянным изменениям, и полагающаяся на такие принципы, как понятность и видимость бизнес-процессов в организации за счет их моделирования с использованием формальных нотаций, использования программного обеспечения моделирования, симуляции, мониторинга и анализа бизнес-процессов, возможность динамического перестроения моделей бизнес-процессов силами участников и средствами программных систем.</p><p>Другими словами, BPM отвечает на вопросы, где, когда, зачем, как и какая задача выполняется и кто отвечает за ее выполнение.</p><p>BPMS (Business Process Management System) – программное обеспечение, обеспечивающее прикладную реализацию концепции BPM.</p><p>BPMN (Business Process Modeling Notation) – нотация (система графических обозначений и их описание в формате XML) бизнес-процесса.</p><p>BPMS на рынке существует великое множество, включая лидеров рынка – Oracle или IBM. Из названия понятно, что эти системы очень мощные, сильные, дорогие и подходят далеко не каждой организации, а стоимость их внедрения может начинаться от 100.000 евро и выше. Понятно, что только крупные компании могут себе позволить внедрить такую систему.</p><ol><li>На мой взгляд небольшим компаниям стоит обратить внимание на другие системы, внедрение которых обойдется в меньшие деньги. Например, российская компания ELMA запустила свой собственный продукт <a href="https://www.elma-bpm.ru/community/edition.html">ELMA BPM</a>, написанный на стеке .NET и имеющий бесплатную версию. Цена на Enterprise версию также вполне демократичная, кроме того, на сайте можно найти обучающие курсы и много другой полезной информации.</li><li>Из зарубежных вендоров с демократическими ценами или бесплатными версиями я бы выделил jBPM.</li><li>И еще одна бесплатная BPM система Аctivity, которую в 2013 году форкнули и из которой получилась известная многим <a href="https://camunda.com/products/camunda-bpm/">Camunda BPM</a>.</li></ol><p>Компания Camunda Services, которая является вендором Camunda BPM, долгое время занималась консультированием организаций в области управления бизнес-процессами, но в результате создала свой продукт и начала активно его развивать.</p><p>В сети есть разные сравнения Аctivity и Camunda и, по большей части, они в пользу последней, да и список компаний, использующих камунду достаточно обширный: Deutsche Bank, Goldman Sachs Group, Тинькофф банк, Сбер, Теле 2 и т.д.</p><p>Мы все понимаем, что бизнес-процесс должен быть максимально понятным. Моделирование процессов происходит с применением нотаций (нотации — это система графических обозначений и их описание в формате XML). Нотаций существует довольно много, но самая популярная из них, конечно, текущая версия BPMN 2.0 (2.0.2 – 2014 г), которая постепенно развивается и пополняется новыми элементами.</p><p>Рассмотрим пример бизнес-процесса, описанном нотацией BPMN:</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_1_Trishechkin.png" alt="" /></figure><p>На картинке изображен процесс регистрации нового пользователя на портале, в достаточно упрощенном формате.</p><p>Здесь нарисован пул процесса и отдельными линиями – дорожки процесса. Каждая дорожка — это отдельная роль бизнес-процессов. Верхняя — это инициатор процессов, посередине – портал и внизу – ответственный за исполнение бизнес-процессов.</p><p>Зеленый кружок — стартовые события процессов, красные кружки – конечные события процессов.</p><p>Прямоугольник — задачи (=таски), которые исполняются во время жизненного цикла бизнес-процесса.</p><p>Задачи со значком шестеренок — сервисные задачи, которые не требуют участия оператора.</p><p>Задачи со значком человечка – это те задачи, которые требуют участия человека.</p><p>Данная система создана для примера и сильно упрощена, в полноценных системах присутствуют и другие элементы, шлюзы, которые реализуют ветвление, есть перехватчики событий, таймеры, и многое другое.</p><h2>Что же такое Camunda BPM?</h2><ul><li>Платформа управления бизнес-процессами, реализующая стандарты BPMN (Business Process Model and Notation), DMN (Decision Model and Notation) и CMMN (Case Management Model and Notation).</li><li>Community версия имеет открытый исходный код, лицензия Apache-2.0.</li><li>Основана на технологическом стеке популярного языка программирования Java. Имеет интеграцию с Spring/Spring Boot.</li></ul><p>DMN – это модель принятия решения, которая представляет собой таблицу входных значений и результирующего выходного значения. Полезна в таких задачах, как принятие решения. Допустим, нужно принять решения о выдаче кредита или открытии счета и именно в этой таблице DMN вы найдете множество вводных параметров и выходной параметр, то есть результат – выдавать кредит или нет.</p><h2>Компоненты CAMUNDA BPM (Community)</h2><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_2_Trishechkin.jpg" alt="" /></figure><p>Давайте подробнее рассмотрим компоненты комьюнити версии Camunda:</p><ol><li>Camunda Modeler – кросс-платформенное приложение с графическим интерфейсом, в котором бизнес-аналитик или архитектор процесса может моделировать и\или редактировать схемы процессов. Поддерживает нотации BPMN 2.0, DMN 1.1, CMMN 1.1. Это отдельное кросс-платформенное приложение с графическим интерфейсом, в котором бизнес-аналитик или архитектор процесса может составлять и\или редактировать схемы процессов.</li><li>Camunda Tasklist — веб-приложение, через которое конечные пользователи могут зайти, посмотреть какие задачи на у них назначены и выполнить какие-то действия.</li><li>Camunda Cockpit – веб-приложение, которое создано для операторов или владельцев бизнес-процессов, обеспечивающее мониторинг и управление бизнес-процессами</li><li>Camunda Admin – веб-приложение, интерфейс администратора системы, в котором идет управление ролями, пользователя, группами и т.д.</li><li>Центральная часть системы Camunda BPM – это движок, который управляет процессом в соответствии с нарисованными схемами и нотациями CMMN и тд, и преобразует их в код, а потом — исполняет.</li><li>Обратите внимание, что над движком существует надстройка, которая реализует REST или Java API. REST/Java API — программный интерфейс системы, обеспечивающий взаимодействие как с внешними (REST), так и внутренними (Java) компонентами. Rest API достаточно обширный, удобный и позволяет реализовывать всякие кастомные приложения. Например, там можно написать свой таск-лист, но подробнее мы рассмотрим эту возможность чуть позже.</li></ol><h2>Компоненты Camunda BPM (Enterprise)</h2><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_3_Trishechkin.jpg" alt="" /></figure><p>Enterprise версия – платная, договор, тех поддержка 24\7 и в техническом плане она получше комьюнити версии. Например, появляются:</p><p>Enterprise Cockpit — расширенная версия Camunda Cockpit, содержащая в себе дополнительные функции: развертывание описаний процессов, перезапуск экземпляров процессов, расширенный поиск по экземплярам процессов, миграция экземпляров процессов между версиями.</p><p>Camunda Optimize — оптимизация и улучшение бизнес-процессов, выявление «узких мест», отчеты и «тепловые карты» процессов, все это помогает улучшить, оптимизировать и модернизировать бизнес-процессы.</p><h3>Пример тепловой карты процесса</h3><p>Тепловая карта показывает относительную частоту выполнения той или иной задачи в процессе. Она помогает визуализировать результат и принять решение, какой участок более нагружен и за счет каких ресурсов\действий его можно разгрузить.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_4_Trishechkin.jpg" alt="" /></figure><h2>Способы развертывания Camunda BPM</h2><p>Как я ранее писал, Camunda BPM написана на тех. стеке Java, поэтому у нее есть много способов развертывания.</p><p>Первый и самый простой – встроенная библиотека в приложении и именно его мы будем подробно разбирать.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_5_Trishechkin.jpg" alt="" /></figure><p>Второй способ тоже подходит для Java приложений — сервис внутри сервера приложений или контейнера сервлетов («расшаренный» сервис), который могут использовать другие приложения, развернутые в контейнере.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_6_Trishechkin.jpg" alt="" /></figure><p>Третий способ, если приложения написаны не на Java, развернуть Camunda как отдельно стоящий сервер и приложение будут с ним контактировать по Rest API удаленно.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_7_Trishechkin.jpg" alt="" /></figure><p>Четвертый способ для высоконагруженных систем — кластерное развёртывание для высоконагруженных систем, когда есть общая база данных и несколько нод с камундой.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_8_Trishechkin.jpg" alt="" /></figure><h2>Практическая часть – пробуем Camunda своими руками</h2><ol><li>Заходим на <a href="https://start.camunda.com/">https://start.camunda.com/</a>.</li><li>Знакомимся с Initializr, который достаточно примитивный, но рабочий. В нем вводим — группу, артефакт, версию Сamunda, версию Java (она никак там не ограничивается, я пробовал java 135 — все нормально, проект сгенерировался). Хочу обратить ваше внимание, что другие языки программирования кроме Java, Scala, Kotlin, выбрать нельзя.<a href="https://media.tproger.ru/uploads/2021/01/img1.png"></a></li><li>Скачиваем и открываем сгенерированный проект.<a href="https://media.tproger.ru/uploads/2021/01/Ris_10_Trishechkin.png"></a></li></ol><p>Внутри мы видим один класс Application — @SpringBootApplication, как я и писал ранее, камунда хорошо интегрируется со SpringBoot. А также в проекте есть файл с настройками приложения и файл с описанием процесса в формате BPM.</p><p>Процесс состоит из следующих этапов: стартовое событие, пользовательская задача, конечное событие. Пользовательская задача, самая простая, выглядит так: пользователь открыл задачу, нажал – завершить задачу и процесс завершился.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img2.png" alt="" /></figure><p>Но давайте немного усложним задачу и внесем изменения в процесс. Процесс мы сделаем процессом регистрации пользователя на портале, тот пример, о котором мы говорили в начале статьи и к которому обещал вернуться. Для этого нам нужно изменить стартовое событие и добавить туда переменную «username», которая добавляется через Camunda Modeler (кросс-платформенное решение, скачивается с официального сайта, бесплатное)</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_11_Trishechkin-2.png" alt="" /></figure><p>В результате у нас получилось модифицировать стартовое событие. Дальше необходимо модифицировать пользовательскую задачу, тут же добавляем форму с именем пользователя. Причем имя пользователя мы делаем «read only», чтобы тот человек, который будет утверждать создание нового пользователя не мог изменить его имя. А также добавляем переменную «decision», которая позволяет утвердить или отклонить эту регистрацию.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_12_Trishechkin.png" alt="" /></figure><p>Запускаем экземпляр процесса. Для запуска экземпляра процесса заходим в приложение Camunda Tasklist (веб-приложение, через которое конечные пользователи могут зайти, посмотреть какие задачи на них назначены и выполнить какие-то действия) и нажимаем «Запустить процесс».</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_13_Trishechkin.jpg" alt="" /></figure><p>Находим свой демо процесс, который мы создали и видим форму запуска процесса.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img3.png" alt="" /></figure><p>После этого появляется окошко для ввода имени пользователя (переменная username), заполняем его и запускаем процесс.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img4.png" alt="" /></figure><p>Процесс запущен, и мы видим, что на пользователя Demo упала задача. Задача, как и ожидается называлась отклонить или утвердить регистрацию. Выделяем ее и видим форму, которую мы создавали. Также мы видим имя пользователя, которое нельзя редактировать, а решение – можно. Завершаем задачу, нажав кнопку «Complete» и на этом процесс завершается.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_14_Trishechkin.jpg" alt="" /></figure><p>Давайте добавим немного экшена в процесс и усложним задачу – создадим ветвления.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_15_Trishechkin.jpg" alt="" /></figure><p>Для этого добавляем к существующему процессу — шлюз, который будет «ветвить» этот алгоритм:</p><ul><li>Если decision равен true, тогда создать нового пользователя.</li><li>Если decision равен false, то просто отправить уведомление о том, что пользователь не создан.</li></ul><p>И также создаем конечное событие. В результате у нас появляются два сервисных таска: создать нового пользователя и отправить уведомление. Сервисные таски, как я уже писал ранее, это таски, которые не требуют участия оператора в их выполнении. Следовательно, в них должна быть заложена логика и здесь мы покажем простой способ взаимодействия с Java кодом.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img5.png" alt="" /></figure><p>Выделяем модуль или сервисный таск и в нем прописываем implementation, выбирая способ Delegate Expression и добавляем в Delegate Expression собственное имя. Собственно этот бин нам и нужно создать в коде.</p><p>Для этого создаем класс CreateNewUser, помечаем его как компонент, чтобы спринг создал в контексте соответствующий бин, а этот класс должен имплементировать JavaDelegate.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img6.png" alt="" /></figure><p>В JavaDelegate есть метод execute, который и будет выполняться, когда поток выполнения придет в этот сервисный таск. На примере выше мы видим, что из переменной DelegateExecution можно вытащить переменную процесса, то есть username, которую мы вводили при старте процесса. Если все пройдет успешно, то в логе мы увидим сообщение, что пользователь успешно зарегистрирован.</p><p>Аналогично поступим в другим сервисным таском – отправить уведомление.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_16_Trishechkin.png" alt="" /></figure><p>Также пропишем Delegate Expression, добавляем ему имя бина «notifyUser» и создаем соответствующий компонент, реализующий JavaDelegate в коде.</p><p>Внутри метода execute мы возьмем переменную decision, также через объект DelegateExecution, и в зависимости от этой переменной вы увидите разные сообщения:</p><ul><li>если decision равен true, то мы видим «, добро пожаловать на портал!»</li><li>если decision равен false, то мы видим «, вам отказано в регистрации»</li></ul><p>Вот этот способ через DelegateExecution работает только в случае с Java приложениями.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_17_Trishechkin.png" alt="" /></figure><p>Возникает вопрос: а что делать, если приложение не на Java или если Camunda развернута на отдельном сервер?</p><p>В этом случае выбираете способ имплементации не Delegate Expression, а External и просто указываете какой-то топик, куда будет отправлен месседж и, соответственно, через очереди будет делегироваться выполнение в ваш код.</p><p>Последний шаг – запустить все то, что мы создали. Вводим имя</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img7.png" alt="" /></figure><p>Видим таск «утвердить\отклонить регистрацию». Допустим, мы отклоняем регистрацию. Тогда не ставим галочку в поле «Утвердить?» и нажимаем «Complete».</p><figure><img src="https://media.tproger.ru/uploads/2021/01/img8.png" alt="" /></figure><p>После этого переходим в логи и видим, что пользователю Дарт Вейдер отказано в регистрации</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_18_Trishechkin.jpg" alt="" /></figure><p>Если мы принимаем регистрацию, то ставим галочку в поле «Утвердить?» и нажимаем «Complete». В этом случае процесс исполняется по другой ветке и в логах отображается по-другому.</p><figure><img src="https://media.tproger.ru/uploads/2021/01/Ris_19_Trishechkin.jpg" alt="" /></figure><h2>Вместо вывода</h2><p>Выше я описал самую простую схему процесса. В реальности же системы намного сложнее с множественными ветвлениями, а в эти диаграммы BPM можно вставлять другие таски, которые могут реализовать нотации DMN.</p><p>Camunda не позиционирует себя как low-code или no-code система. Camunda позиционирует себя как систему, где должно быть разумное сочетание, своеобразный симбиоз, между бизнесом, аналитиками и разработкой.</p><h2>Полезные ссылки</h2><ul><li><a href="https://docs.camunda.org/get-started/">Быстрый старт</a></li><li><a href="https://docs.camunda.org/manual/7.13/reference/bpmn20/">Описание стандарта BPMN0</a></li><li><a href="https://docs.camunda.org/manual/7.13/reference/rest/">Описание Rest API</a></li><li><a href="https://www.elma-bpm.ru/journal/prakticheskij-kurs-bpmn-soderzhanie/">Практический курс BPMN0 от российской компании ELMA</a></li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Стоит прочитать: обзор книги Бретта Маклафлина «Объектно-ориентированный анализ и проектирование»</title>
      <link>https://tproger.ru/books/obzor-knigi-bretta-maklaflina-obektno-orientirovannyj-analiz-i-proektirovanie</link>
      <comments>https://tproger.ru/books/obzor-knigi-bretta-maklaflina-obektno-orientirovannyj-analiz-i-proektirovanie?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/books/obzor-knigi-bretta-maklaflina-obektno-orientirovannyj-analiz-i-proektirovanie</guid>
      <description><![CDATA[<p>Издание серии Head First о том, как устроены анализ и проектирование объектно-ориентированных программ, полезно разработчикам junior и middle.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/books/obzor-knigi-bretta-maklaflina-obektno-orientirovannyj-analiz-i-proektirovanie">Стоит прочитать: обзор книги Бретта Маклафлина «Объектно-ориентированный анализ и проектирование»</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Объектно-ориентированное программирование]]></category>
      <category><![CDATA[Стоит прочитать]]></category>
      <category><![CDATA[Книги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 17 Dec 2020 12:45:45 GMT</pubDate>
      <content:encoded><![CDATA[<p>Пожалуй, одной из самых важных книг в моей карьере я могу назвать «Объектно-ориентированный анализ и проектирование» из серии Head First, авторы Бретт Маклафлин, Гэри Поллайс и Дэйв Уэст. Вообще Head First очень полезная серия книг для разработчиков уровня junior/middle, она способна подтолкнуть их по карьерной лестнице в мир бизнеса, архитектурных решений и большой ответственности в рамках всего продукта. Но для начала хочу остановиться на небольшой части этого мира, а именно – на приложении.</p><h2>С чего начинается приложение?</h2><p>Авторы рассказывают о том, что приложение начинается с потребностей заказчика, кем бы он ни был представлен. Владелец банковской карты, геймер, врач, официант — у всех них есть боли и потребности. В книге объясняется, почему важно правильно собрать требования, как сильно это влияет на проектирование приложения и что будет, если этого не сделать (спойлер – ничего хорошего:). Изучать этот вопрос вы начнете с приложения для одного заказчика, владельца гитарного магазина, которому нужно сделать систему учета своих гитар, и ваша цель – помочь ему в этом. Когда решите эту задачку, перейдете к более сложным примерам, которые при этом вполне понятные и подробно описаны. Кстати, хочу обратить внимание на подачу материала: он простой, язык близок к разговорному, а еще в книге много иллюстраций и схем, и это здорово помогает восприятию. В некоторых упражнениях книгу предлагают использовать как рабочую тетрадь, что тоже удобно (если только вы не читаете ее в электронном виде).</p><h2>Про требования и их изменчивость</h2><p>Авторы «Объектно-ориентированного анализа и проектирования» идут вместе с читателем по всем этапам жизненного цикла – от сбора требований заказчиков до тестирования готового решения.</p><p>Чуть подробнее расскажу про первый из этих этапов. «Как собирать требования? Что предусмотреть? А какие части могут меняться?» — эти вопросы программист задает себе не всегда, а стоило бы. Потому что от грамотного и подробного сбора требований зависит качество результата и удовлетворенность заказчика. Книга акцентирует внимание на этих вещах, и показывает, как заложить гибкость и изменчивость в код, чтобы доработки не заставали вас врасплох.</p><p>Как известно из практики и другой хорошей книги, «Совершенный код» Стива Макконелла, самые дорогие ошибки случаются из-за некорректных требований или из-за недосмотра потенциальных изменений. В нашем мире, разумеется, нельзя всё предусмотреть, но хотя бы попытаться — точно стоит!</p><h2>А где же разработка?</h2><p>Наверное, я вас уже напугал, что в книге не будет ни строчки кода. Но нет, в ней приведено очень много примеров и хорошего, и плохого кода, и советов как отличить один от другого. Плюс в книге много практических заданий на весь изученный материал – в каждой главе есть задачки, на которых можно применить свежие знания. А в последней четверти книги нас ждет большой проект, который предстоит сделать полностью самостоятельно – от сбора требований и до красиво структурированного кода.</p><p>Кстати, о структурированном коде. В книге подробно изложены важные моменты:</p><ul><li>основные принципы ООП — даже начинающий программист слышал про инкапсуляцию, наследование и полиморфизм. Тут вы закрепите свои знания, и поймёте, что инкапсуляция — это не только про область видимости;</li><li>принцип DRY — Don’t repeat yourself, он расскажет, как избежать повторения кода и инкапсулировать логику правильно;</li><li>принцип KISS — keep it simple stupid, здесь пойдет речь о том, почему не стоит строить слишком гибкие приложения, пытаясь предвидеть требования;</li><li>принцип YAGNI — You ain’t gonna need it, это история о том, как не писать лишний код, пытаясь играть в экстрасенса;</li><li>паттерны проектирования — важная составляющая, которая поможет вам использовать мудрость других разработчиков (:</li></ul><p>Все это показано на примерах из реальной жизни. Замечу, что с этой информацией вы, скорее всего, столкнетесь на собеседовании, что делает ее еще более ценной.</p><h2>Послесловие</h2><p>Мир программирования довольно обширен и описать его в одной книге, даже самой подробной, довольно сложно. Тем не менее, авторам удалось уложить в нее много важных моментов, которые однозначно пригодятся на практике программистам разного уровня. Кому-то будут полезны паттерны и принципы чистого программирования, кому-то пригодится анализ требований, кому-то — проектирование в UML.</p><p>Но что точно пригодится каждому, — понимание, что программы не бывает без заказчика.</p>]]></content:encoded>
    </item>
    <item>
      <title>Software Architect: рассказ от первого лица о новой, но очень востребованной профессии</title>
      <link>https://tproger.ru/articles/software-architect-rasskaz-ot-pervogo-lica-o-novoj-no-ochen-vostrebovannoj-professii</link>
      <comments>https://tproger.ru/articles/software-architect-rasskaz-ot-pervogo-lica-o-novoj-no-ochen-vostrebovannoj-professii?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/software-architect-rasskaz-ot-pervogo-lica-o-novoj-no-ochen-vostrebovannoj-professii</guid>
      <description><![CDATA[<p>Три архитектора ПО из компании Acronis в России, Сингапуре и США говорят о зарплатах, условиях работы и о том, зачем продукту архитектура.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/software-architect-rasskaz-ot-pervogo-lica-o-novoj-no-ochen-vostrebovannoj-professii">Software Architect: рассказ от первого лица о новой, но очень востребованной профессии</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Карьера]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 10 Dec 2020 15:48:33 GMT</pubDate>
      <content:encoded><![CDATA[<p>рассказывает Анна Мелехова, Acronis Software Architect</p><p>Когда студенты приходят в вуз, им не рассказывают, кто такой Software Architect, чем он занимается, и сколько ему платят. И это не удивительно, ведь потребность в архитектуре нарастала по мере увеличения сложности продуктов. Сегодня об этой профессии, зарплатах и условиях работы в разных странах расскажут три специалиста из компании Acronis, которые выполняют роли программных архитекторов в России, Сингапуре и США.</p><p>Системный подход к архитектуре не обязателен для небольших организаций и для простых продуктов , когда их создатель может удержать в своей голове всю важную информацию о структуре и принципах работы решения. Однако по мере усложнения любой технологической системы, потребность в архитектуре возрастает, как в средстве для борьбы со сложностью. Именно поэтому многие считают пост или роль архитектора вершиной технической карьеры. Но далеко не всегда понятно, как стать этим самым архитектором.</p><p>Кстати, архитекторы тоже бывают разными — в компаниях можно найти архитекторов решений, архитекторов систем, главных корпоративных архитекторов, архитекторов ПО, архитекторов БД. Каждый из этих титулов говорит о том, что специалист занимается выстраиванием процессов и взаимодействий в своей сфере на самом высоком уровне. Архитектор в любом случае должен иметь большой кругозор, так как он принимает сложные технические решения. Ему должен доверять бизнес, ведь часто в необходимости вкладываться в развитие архитектуры нужно убедить всех участников процесса.</p><p>И если Solution-архитекторы встречаются достаточно часто, то Software-архитектор, который требуется каждой компании, в которой ведется масштабная разработка программного обеспечения, оказывается дефицитной специализацией. Именно поэтому на сервисах по подбору персонала можно найти немало вакансий с хорошей зарплатой и названием Software Architect. А на вопрос, как же стать таким специалистом, и в какой стране лучше работать с подобной квалификацией, расскажут герои нашего материала.</p><p>Я не сразу стала архитектором: долгое время я вообще не представляла, что существует такое направление со своей объемной теоретической базой, сложившимися практиками и концепциями. Но когда я работала в компании Parallels на должности lead developer, в компанию пришел Майк Тотонги (Michael Toutonghi), который начал говорить об архитектуре ПО, как об отдельной дисциплине, я начала изучать материалы, знакомиться с концепциями и нарабатывать опыт. Оказалось, что многие вещи я уже делала как архитектор, но не знала правильных названий и была ограничена своим опытом и опытом коллег. Чтение литературы/лекции позволило расширить границы.</p><p>С 2018 года я начала работать в швейцарско-сингапурской компании Acronis именно как архитектор ПО, а точнее новой платформы, которую мы продолжаем развивать.</p><h2>Обязанности</h2><p>Как архитектор платформы, я прежде всего забочусь о том, чтобы эта платформа выглядела для внешних пользователей просто. Мой главный фокус был на выстраивание API. В мои обязанности входило налаживание процессов, которые помогут сделать внутреннее API хорошо документированным, согласованным, скоординированным, протестированным и публичным. Кроме организации процессов, мы с коллегами также участвовали в проектировании API и «популяризации» правильных подходов к созданию интерфейсов внутри компании.</p><h2>Зарплата и национальные особенности</h2><p>Сразу скажу, что единой сетки зарплат для программных архитекторов не существует. Размер оклада может быть разным в зависимости от размеров компании, масштабов создания ПО, а также количества команд разработчиков. Например, в Москве архитектор ПО может получать от 220 тысяч рублей в месяц, при этом можно найти вакансию со значительно большей зарплатой.</p><p>Стоит учитывать, что в России и СНГ существует дефицит кадров в области программной архитектуры, ведь этому почти никто не учит, нет такого профиля высшего образования. Поэтому даже очень привлекательные вакансии остаются открытыми много месяцев.</p><h2>Кому подходит быть архитектором</h2><p>Быть программным архитектором — значит постоянно расширять свой кругозор, мыслить системно, а также находить компромиссы и уметь принимать чужую точку зрения. Архитектор не может быть приверженцем какой-то технологии. Он не будет навязывать свои личные технические предпочтения. Архитектор редко говорит «нет», потому что он уже слишком много видел и прекрасно знает, что практически в любом решении есть и плюсы, и минусы. Главная задача архитектора — проявить их и соотнести с целями компании. Software-архитектор должен быть гибким и ориентированным на поиск компромисса, но не «мутным». Важное качество архитектора — явно доносить все возможные последствия от каждого шага, и если принятое решение имеет много минусов, он должен убедиться что руководство понимает цену и сознательно идет на этот шаг.</p><p>Поэтому не все могут быть архитектором просто по складу личности. Я бы советовала почитать такую книгу, как «Software architecture in practice» от профессоров Carnegie-Mellon University. Если она покажется вам слишком дотошной, значит архитектура — не ваше призвание.</p><h2>Как стать программным архитектором</h2><p>Путь в системные архитекторы часто лежит через должность старшего разработчика, Чтобы совершить это рывок нужна прокачка Soft Skills и расширение своей экспертизы. Но существует и другой путь, и теперь на архитектора можно даже выучиться. Например, я веду курс программной архитектуры в университете «Иннополис». После такого обучения можно стать Junior Software Architect с уже «поставленным» критическим и системным мышлением, а реальный опыт накапливать уже в процессе работы.</p><p>Устроиться сразу после университета в хорошую компанию на должность Software Architect очень сложно, потому что на этой должности требуется большой опыт. Как и в случае с Software Engineer, QA или Product Manager, более опытный кандидат стоит дороже и, вероятно, будет решать поставленные задачи эффективнее. Сегодня я выполняю роль архитектора ПО в Acronis, находясь в американском офисе компании. И я уверен, что достичь этого мне помог именно предыдущий опыт.</p><p>Свой путь в сфере Software Engineering я начал больше 10 лет назад. На работу в компанию Acronis я пришел как Software Developer. После этого меня повысили до старшего разработчика, а после и до Team Lead’а. Сейчас я нахожусь на весьма необычной должности, которая совмещает в себе большую часть роли Software Architect и немного от программного менеджера.</p><p>Я бы сказал, что позицию Software Architect не стоит рассматривать как «вершину» пути разработчика. По пути Software Engineer можно достичь столь же высокой оплаты и быть ценным для компании. К тому же, любая должность (независимо от ее названия) — это всегда совмещение нескольких ролей, но  их баланс будет всегда различным. Software Architect это весьма сложная и интересная позиция, которая включает в себя много разных ролей.</p><h2>Обязанности</h2><p>Личной мой баланс задач на сегодняшний день состоит на 60% из задач выбора и внедрения архитектуры, и на 40% из анализа рынка, работы с другими продакт-менеджерами. Я трачу достаточно много времени на то, чтобы понять потребности рынка, а также транслировать требования к разработчикам на технический язык.</p><p>В моей ситуации уже не остается времени на непосредственное участие в разработке продукта, но при этом я продолжаю тесно работать со многими девелопмент-команд. Но я понимаю, что разработка могла бы и присутствовать в среди ежедневных задач, если бы соотношение ролей на моей позиции было бы иным.</p><h2>Зарплата и национальные особенности</h2><p>Зарплата архитектора ПО в США так же, как и оплата других позиций в сфере software development, находится на достаточно высоком уровне. Несмотря на то, что зарплаты архитекторов в среднем выше, чем у разработчиков, зачастую сетки окладов пересекаются — все зависит от компаний и конкретных позиций. Я бы сказал, что стремиться к должности программного архитектора только из-за дохода не имеет смысла.</p><p>При этом поиск хорошего архитектора ПО для стартапа или небольшой компании может быть непростой задачей. Да, такие гиганты как Google или Microsoft собирают лучшие кадры с рынка, но остальным приходится искать специалиста с релевантным опытом. А это не всегда быстро и просто.</p><h2>Кому подходит быть архитектором?</h2><p>Архитектору ПО важно понимать базовые основы операционных систем и сетей, знать и владеть разными языками программирования, знать и уметь применять (или наоборот, уметь не применять) общепринятые подходы в программирование и проектировании. Как говорится, нужно быть в тренде всего технического. Это все то, что должен уметь и опытный разработчик.</p><p>Но если разработчик может себе позволить быть «узким» специалистом, то для архитектора это недопустимо. Архитектор должен уметь разбираться в любой технической проблеме, документировать и аргументировать любые решения, уметь выслушивать позиции других людей и помогать бизнесу достигать своих целей. Эта работа подходит для стрессоустойчивых и коммуникативных людей с широким спектром интересов и прокачанными Soft Skills.</p><h2>Как стать архитектором ПО</h2><p>Сейчас появляются различные учебные программы, и люди все больше начинают говорить об архитектуре. Но, на мой взгляд, стать хорошим архитектором, не побывав разработчиком, вряд ли получится. Поэтому нужно сначала попробовать себя в разработке и при наличии склонностей к широкому подходу, расширять свою экспертизу, интересоваться всеми новыми технологиями и развивать Soft Skills.</p><p>Я работаю в отрасли уже 16 лет, и начинал как бэкенд-разработчик. Перед тем, как стать архитектором ПО, я побывал в разных компаниях на руководящих ролях, в том числе относящихся к R&amp;D.</p><p>Значительную часть моей карьеры я занимался дизайном продуктов, в том числе — низкоуровневым. И я считаю, это создало важную основу для того, чтобы стать архитектором ПО. Потребность приводить продукт в соответствие требованиям заказчиков заставляла искать новые подходы, рассматривать разные технологии, разбираться с архитектурой и шаблонами дизайна.</p><h2>Обязанности</h2><p>Сейчас в мои обязанности входит контроль за развитием приложений и сервисов. Необходимо обеспечить масштабируемость, провести правильный выбор технологий, обеспечить надежность и преемственность. Сейчас в Acronis я работаю над тем, чтобы обеспечить бесшовную интеграцию компонентов, а также гарантировать создание новых приложений в соответствии с принятой архитектурой.</p><h2>Зарплата и национальные особенности</h2><p>У меня есть опыт работы в Индии и Сингапуре. До начала своей карьеры в Acronis я работал в таких крупных фирмах, как Veritas и Avaya. Практика показывает, что в Дели достаточно легко найти программного архитектора, так как на рынке труда достаточно квалифицированных кадров. Как следствие, в Индии зарплата программного архитектора не будет значительно отличаться от оплаты труда другого технического специалиста аналогичного уровня и квалификации. В Сингапуре «вилка» может быть более значительной.</p><p>В Сингапуре намного проще найти архитектора решений. ИТ-отрасль больше ориентирована на сервис, и поэтому культура создания архитектуры решений намного более развита. Архитекторы ПО здесь встречаются реже. Но зато специалисты больше ориентированы на новые технологии. Здесь проходит больше митапов, на которых обсуждаются облачные решения, контейнеры, современные языки программирования.</p><p>Но несмотря на все это в большинстве случаев программные архитекторы следуют общим практикам, принятым в той организации, в которую они устроились работать. И заработная плата программного архитектора, на мой взгляд, зависит от компании больше, чем от страны, особенно если речь идет о международных организациях. Поэтому оклад и должностные обязанности двух программных архитекторов в одной и той же стране могут оказаться совершенно разными.</p><h2>Кому подойдет быть программным архитектором?</h2><p>Чтобы быть архитектором нужно хорошо разбираться в структурах данных, понимать требования низкоуровневого дизайна. Эта специальность подойдет тем, кто готов вести исследования новых технологий в самых разных областях, изучать аспекты производительности, работу распределенных систем.</p><p>Архитектором может стать тот, кто тяготеет к разработке решений, а не просто приложений. Обычно программные архитекторы обладают опытом в области разных технологий: они знают про работу прокси, нюансы создания приложения, методы  защиты, особенности корпоративной шины (ESB) и так далее.</p><p>Но самое главное, что программному архитектору нужно принимать взвешенные решения, аргументировать свое мнение. Мы в сингапурском офисе Acronis работаем со всем новым активно, уже сформирован процесс архитектурной разработки. Но на практике встречаются разные стили работы, я хорошо знаю, что некоторых организациях нужно больше аргументировать свои мнения, предлагать прототипы, работать над этим.</p><p>Становиться архитектором стоит только если вы готовы к сопротивлению, хотите договариваться со всеми участниками процесса, включая самих разработчиков. Лично я сталкивался с разного рода сопротивлением к масштабным изменениям, и в среднем, на мой взгляд, его уровень — 4 из 10. Так что архитектору ПО нужна определенная стрессоустойчивость.</p><h2>Как стать программным архитектором?</h2><p>Чтобы стать архитектором ПО нужно смотреть на технологии максимально широко, расширять свою экспертизу горизонтально, исследовать все актуальные тренды — облачные технологии, блокчейн, новые фреймворки. Если техлиды должны копать вглубь, вертикально, архитекторам это просто противопоказано. Они должны оставаться на поверхности, охватывая максимальное количество взглядов, концепций и подходов.</p><p>Чтобы стать хорошим архитектором ПО нужно непрерывно изучать и исследовать новые технологии и решения, изучать новые требования, варианты дизайна, готовые решения проблем.</p><p>В качестве источника важных знаний я бы рекомендовал такие книги как Software Architecture in Practice и Designing Software Architectures: A Practical Approach. Кроме этого для самообразования будет полезна документация успешных облачных провайдеров. Лично я использую концепции AWS, чтобы узнать больше о новых подходах к архитектуре ПО.</p>]]></content:encoded>
    </item>
    <item>
      <title>Стоит прочитать: обзор книги Роберта Мартина «Чистая Архитектура»</title>
      <link>https://tproger.ru/books/obzor-knigi-roberta-martina-chistaja-arhitektura</link>
      <comments>https://tproger.ru/books/obzor-knigi-roberta-martina-chistaja-arhitektura?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/books/obzor-knigi-roberta-martina-chistaja-arhitektura</guid>
      <description><![CDATA[<p>Книга «дядюшки Боба» о дизайне и проектировании программ и о том, как вести разработку в условиях постоянно меняющихся требований заказчика.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/books/obzor-knigi-roberta-martina-chistaja-arhitektura">Стоит прочитать: обзор книги Роберта Мартина «Чистая Архитектура»</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Стоит прочитать]]></category>
      <category><![CDATA[Книги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 04 Dec 2020 12:34:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Не секрет, что одним из самых сложных в разработке программного обеспечения, является вопрос «как успешно вести разработку в условиях изменяющихся требований?»</p><p>Изменяющиеся требования среди многих разработчиков принято считать чем-то очень плохим и даже в какой-то мере неадекватным. Тот факт, что пожелания клиентов менялись в процессе разработки системы, принято считать основной причиной плохой архитектуры, нестабильного поведения, множества неисправностей и жалоб клиентов, а также снижающаяся с каждым годом скорость разработки, и в то же время всё возрастающая стоимость добавления новой функциональности.</p><p>Понятие Чистой архитектуры впервые Роберт Мартин (или, дядюшка Боб) <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">опубликовал</a> у себя в блоге ещё в 2012 году. В книге же, он подводит к этому понятию последовательно, в первую очередь развеивая миф о катастрофичности изменчивых требований. Чтобы сделать это, он вводит несколько важных понятий и определений.</p><p>С самого начала он говорит о том, что всякая программа имеет две ценности: <b>поведение</b> и <b>структуру (архитектуру)</b>. И хотя разработчики отвечают за высокий уровень обеих этих ценностей, важно понимать, что на успешность развития проекта одна из ценностей влияет сильнее другой. Какая?</p><p>Многие программисты полагают, что их задача заставлять компьютеры соответствовать требованиям и исправлять ошибки под отладчиком. Однако, на самом деле, программистов нанимают на работу, чтобы они заставили компьютеры экономить деньги или приносить прибыль. Это определяет <b>поведение</b> — одну из двух ценностей программы.</p><p>Но попробуйте ответить на вопрос: что лучше, программа, которая работает идеально, но её невозможно изменить при смене требований? Или же программа, которая не работает, но её легко поменять?</p><p>В книге даётся аргументированный ответ о том, что изменяемость программного обеспечения – его важнейшее свойство, которое исходит из самого определения английского слова Software: Soft — «гибкое / программное» + Ware — «обеспечение». Идея программного обеспечения состоит в том, чтобы дать простую возможность изменять поведение компьютеров. Это и делает ПО гибким. И это такое же требование, как и любые бизнес-требования, которые должны удовлетворить разработчики и архитекторы, создав соответствующую <b>структуру (архитектуру)</b> продукта.</p><p>Бизнес менеджеры не способны осознать важности архитектуры, потому что они «продают» <b>поведение</b>. По этой же причине они всегда ставят важность поведения на первое место и стараются повысить приоритет соответствующей работе. Автор же уверяет, что о важности качества архитектуры обязаны говорить разработчики, так, как только они наиболее полно представляют возможные негативные последствия того, что архитектуре не будет уделено достаточное внимание. Они должны бороться за неё. На самом деле им за это и платят.</p><p>Поэтому, эта вторая ценность <b>(структура)</b>, сильнейшим образом влияет на дальнейшую судьбу проекта, хоть это сначала и трудно заметить.</p><p>Роберт Мартин также вводит понятия <b>масштаба</b> и <b>формы</b> изменений. Он говорит о том, что если требования меняются, то сложность изменений должна быть пропорциональна лишь масштабу изменения, но никак не его форме. Именно эта разница между масштабом и формой часто является причиной роста стоимости разработки программного обеспечения.</p><p>Опять же, с точки зрения Бизнес Менеджера, он лишь создаёт постоянный поток новых требований одного и того же масштаба. Однако разработчикам кажется, что их вынуждают постоянно добавлять всё новые «костыли» в деградирующую систему. Но на самом деле, проблема в самой архитектуре, которая предусматривает предусматривает возможность внесения только одной формы изменений. Архитектуры должны быть максимально независимыми от формы. В дальнейших главах читатель узнает о том, как же этого достичь.</p><p>Мне понравилось, что автор последователен и начинает как бы издалека: он рассуждает про все известные нам парадигмы программирования и то, что важного они привнесли с точки зрения архитектуры систем. Он детально объясняет понятия зависимостей в программе, потока данных программы, а также касается возможностей инверсии зависимостей (Dependency Injection) в программе. В итоге он делает вывод о том, что самое важное что привнесла парадигма ООП (объектно-ориентированное программирование) – это возможность полного контроля над направлением зависимостей между частями и компонентами программы.</p><p>Как любое здание состоит из кирпичиков, так и любая хорошая программная система начинается с чистого кода. Несколько глав книги в деталях знакомят читателя с пятью основными принципами дизайна кода на уровне структур данных (классов и функций), в мире известные как SOLID:</p><ul><li>Принцип единственной ответственности (SRP).</li><li>Принцип открытости/закрытости (OCP).</li><li>Принцип Барбары Лисков (LSP).</li><li>Принцип разделения интерфейса (ISP).</li><li>Принцип инверсии зависимости (DIP).</li></ul><p>Далее, автор подробно рассказывает про понятие компонент системы и рассказывает о трех принципах, на основе которых принимается решение к какому компоненту отнести тот или иной класс:</p><ul><li>Принцип эквивалентности повторного использования и выпусков (REP).</li><li>Принцип согласованного изменения (CCP).</li><li>Принцип совместного повторного использования (CRP).</li></ul><p>Эти три принципа определяют так называемую связность компонент (Component Cohesion). И следуя логике, дядюшка Боб описывает сочетаемость компонент (Component Coupling), рассказывая о трех принципах, определяющих правила взаимоотношений между компонентами:</p><ul><li>Принцип ацикличности зависимостей (Acyclic Dependencies Principle).</li><li>Принцип устойчивых зависимостей (Stable Dependencies Principle).</li><li>Принцип устойчивости абстракций (Stable Abstractions Principle).</li></ul><p>Знакомство с этими принципами подкрепляется практическими рекомендациями о метриках, которые можно высчитать и которые можно контролировать в системе.</p><p>Наконец, автор подходит к теме Архитектуры, рассуждая о её целях и качестве. Выделяется главное предназначение архитектуры — поддержка всего жизненного цикла системы. Хорошая архитектура делает систему легкой в освоении, простой в разработке, сопровождении и развертывании. Конечная ее цель — минимизировать затраты на протяжении срока службы системы и максимизировать продуктивность программиста.</p><p>По словам автора, для успешного существования и развития проекта, хорошая архитектура должна обеспечивать:</p><ul><li>разнообразие вариантов использования (use cases);</li><li>эффективную работу системы;</li><li>простоту разработки системы;</li><li>простоту развертывания системы.</li></ul><p>Достичь баланса во всём крайне сложно, но одним из важнейших шагов обеспечения баланса этих требований является правильное разделение системы на компоненты, а точнее – правильное проведение границ.</p><p>Когда архитектор стремится получить структуру системы, поддерживающую все необходимые варианты использования, он может не знать их все. В этом случае, опираясь на контекст назначения системы, он может использовать принципы единственной ответственности (SRP) и согласованного изменения (CCP) для отделения друг от друга всего, что изменяется по разным причинам, и объединения всего, что изменяется по одной причине.</p><p>Кстати, непонимание принципа SRP является одним из наиболее часто встречаемых на собеседованиях и интервью кандидатов: ведь важно не столько то, что класс имеет одну область ответственности, сколько то, что класс будут меняться по одной причине.</p><p>По разным причинам обычно меняется пользовательский интерфейс, бизнес-правила программы, бизнес-правила самой предметной области, ну и конечно части, отвечающие за ввод-вывод, сетевой доступ или доступ к базам данных Автор называет это <b>Уровнями</b>. Причем, чем дальше от ввода-вывода, тем выше уровень.</p><p>Более того, меняться с разной частотой могут и сами варианты использования, что также требует разделения, например функционал работы корзины и списка товаров в онлайн магазине, или даже функции удаления и создания заказов.</p><p>Разделение на уровни и варианты использования даёт возможности как добавления новых вариантов использования системы, так и возможности независимой разработки и развёртывания. Иначе говоря – делает архитектуру гибкой.</p><p>Роберт Мартин называет это <b>Режимом разделения</b>. В пример таких режимов он приводит сервис-ориентированную архитектуру (SOA), основанную на службах, а также плагинную архитектуру.</p><p>Автор отмечает важность избегания преждевременных решений, которые не касаются бизнес-требований или вариантов использования, но являются чисто техническими, вроде выбора фреймворка, БД, веб сервера и т. д. Хорошая архитектура не зависит от таких решений. Чтобы добиться этого, например, в плагинной архитектуре границы между компонентами проведены так, что для одной и той же программы можно предоставить несколько разных пользовательских интерфейсов, настроить работу на разных БД, и т.д.</p><p>Что же происходит, когда границы компонент пересекаются? А ничего, это нормальный ход событий, когда, например клиент (высокоуровневый компонент) вызывает функцию сервера (низкоуровневого компонента). И совсем не спроста в начале книги подробнейше рассматриваются парадигмы программирования и каждый принцип проектирования: ведь вся хитрость создания правильных решений о пересечении границ между компонентами, заключается <b>в управлении зависимостями</b>.</p><p>Я заметил, что на протяжении всех глав, важности проведения границ в книге уделяется особое внимание. Приведены схемы и диаграммы того, как избегать прямой зависимости между классами или компонентами, например клиентом и серверов. Показано, как четкое отделение интерфейсов от реализации даёт возможность повернуть направление зависимости в обратную сторону потоку управления. Потоки управления и потоки данных, в хорошо спроектированной системе, всегда должны быть развязаны от зависимостей в коде, чтобы удовлетворить упомянутые выше требования: простоту разработки и развертывания системы.</p><p>Хочу отметить, что автор говорит о границах не только в коде и архитектуре, но и в бизнес-требованиях.  Он пишет, что бизнес-требования состоят из сущностей, часто описываемых на языке UML, а также вариантов использования, неформально описываемых в виде входных и выходных бизнес-данных. И те и другие не должны описывать низкоуровневые аспекты системы, вроде UI или фреймворка.</p><p>В итоге, сводя всё вышеизложенное воедино, Роберт приводит пример Чистой архитектуры, по его словам, объединяющей лучшие стороны большинства известных архитектур, таких как гексагональная архитектура, Data-Context-Interaction архитектура, и Boundary-Control-Entity архитектура.</p><p>В дальнейших главах автор разбирает чистую архитектуру в деталях, описывая способы пересечения границ, например используя DTO (Data-Transfer Objects). Он описывает шлюзы к слою доступа к базам данных и преобразователи реляционных данных (ORM). Я отдельно отмечу подробно разобранные паттерны проектирование, такие как:</p><ul><li>View-Presenter;</li><li>Façade (Фасад);</li><li>Strategy (Стратегия);</li><li>Service (Служба);</li><li>CQRS;</li><li>Main component (анти-паттерн «Главный компонент»).</li></ul><p>Наконец, автор приводит примеры использования принципов Чистой архитектуры в решении нескольких конкретных задач, таких как построение игрового движка (Game Engine), сервиса заказа такси (Taxi Aggregator) и сервиса продажи видео. Он также описывает возможности применения чистой архитектуры во встраиваемом ПО и операционных системах, а также проектирование интерфейсов и API для тестирования.</p><p>На мой взгляд, книга покрывает важнейшие аспекты дизайна и проектирования программ. В ней нет обзора конкретных фреймворков или языков программирования, но она показывает на что нужно обращать особое внимание, как закладывать гибкость, выявлять нарушения и контролировать структуру проекта. Так или иначе, любой разработчик столкнётся с необходимостью знания описываемых в книге принципов, но чем раньше он это сделает, тем меньше ошибок он совершит. Именно поэтому я рекомендую прочтение этой книги каждому разработчику, независимо от его уровня – от Junior до Senior и Architect.</p>]]></content:encoded>
    </item>
    <item>
      <title>Кто такой системный архитектор и как им стать</title>
      <link>https://tproger.ru/articles/becoming-a-system-architect</link>
      <comments>https://tproger.ru/articles/becoming-a-system-architect?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/becoming-a-system-architect</guid>
      <description><![CDATA[<p>Системный архитектор выстраивает ИТ-инфраструктуру под задачи заказчика: о навыках и знаниях для этой профессии рассказывает специалист Rubytech.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/becoming-a-system-architect">Кто такой системный архитектор и как им стать</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Карьера]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 11 Sep 2020 10:16:48 GMT</pubDate>
      <content:encoded><![CDATA[<p>Системный архитектор, он же главный инженер проекта (ГИП) — специалист, способный выстроить сложную ИТ-инфраструктуру под индивидуальные потребности заказчика. Разбираемся в тонкостях этой редкой профессии вместе с директором департамента системной архитектуры <a href="https://rubytech.ru/">Rubytech</a> Александром Палкиным.</p><p>Так же, как и в строительстве зданий, главная задача архитектора ИТ-инфраструктуры — спроектировать все так, чтобы конструкция была надежной, элементы идеально сочетались между собой и бесперебойно выполняли свои функции. Это ответственная работа, требующая глубокой экспертизы в ИТ и развитых управленческих навыков.</p><p>Цена ошибки здесь крайне высока. Уязвимости и сбои в ИТ-инфраструктуре крупных заказчиков могут привести к потере прибыли в миллиарды рублей, а в случае с нефтяными компаниями или производствами — и вовсе грозить экологической катастрофой. Системный архитектор — это важный человек, который предотвращает подобные сценарии еще на этапе планирования. Справиться с такой работой может только специалист с высоким уровнем квалификации.</p><p>Так как потребности у каждого бизнеса разные, универсальных алгоритмов и типовых кейсов на рынке практически нет — каждый проект уникален. И несмотря на кажущуюся монотонность, это по-настоящему творческая работа, в которой можно в полной мере реализовать свой потенциал.</p><h2>Чем занимается системный архитектор</h2><p>Системный архитектор подключается к проекту на этапе пресейла и полностью отвечает за всю техническую часть. Он участвует в разработке технико-коммерческого предложения, в деталях продумывает каждый элемент будущей системы. Он же и согласовывает концепцию проекта и «продает» ее заказчику — убеждает его в правильности и оптимальности каждого решения.</p><p>После подписания контракта системный архитектор выступает в роли технического руководителя проекта: собирает команду, распределяет роли и курирует все процессы вплоть до реализации инфраструктуры. Свою работу он ведет в тесном сотрудничестве с руководителем проекта, детально погружаясь в особенности ИТ-инфраструктуры заказчика и учитывая все специфические требования.</p><h2>Где востребованы такие специалисты</h2><p>Везде, где происходит цифровая трансформация, создаются мощные и комплексные ИТ-инфраструктуры. Например, в проектах для крупных госкорпораций, которые отличаются  не только масштабом, но и высоким уровнем критичности и важности.</p><p>Чаще всего такие проекты реализуют силами системных интеграторов, в которых и работают системные архитекторы. В зависимости масштаба проекта, принимать участие в нем может как один, так и несколько системных архитекторов.</p><h2>Ключевые навыки системного архитектора</h2><p>Позиция подразумевает многолетний опыт работы. Поэтому одними только теоретическими знаниями здесь не обойтись. Необходимо понимать, как разные решения сочетаются между собой, где могут возникнуть проблемы, уметь досконально просчитывать риски. Важно видеть картину в целом, но при этом отлично знать каждую составляющую ИТ-инфраструктуры. Наработать это можно исключительно практикой. В случае с госсектором необходимо также учитывать требования, связанные с политикой импортозамещения, и ювелирно внедрять российские решения в уже работающие системы.</p><p>Помимо обширных знаний в ИТ, архитектор должен обладать рядом надпрофильных навыков или soft skills. В первую очередь — коммуникационных. Он должен грамотно формулировать свои мысли как письменно, так и устно. Ему предстоит много общаться заказчиками, вежливо, но настойчиво отстаивать свою точку зрения, ставить задачи и мотивировать команду. А в отдельных случаях — работать в связке с другими архитекторами.</p><p>Необходимо уметь планировать и проводить презентации, «продавать» свои идеи. Еще один важный навык — тайм-менеджмент, так как придется распоряжаться не только своим временем, но и грамотно распределять нагрузку внутри проектной команды.</p><h2>Как стать системным архитектором</h2><p>Поскольку должность подразумевает большой опыт работы с ИТ-инфраструктурой, системными архитекторами не становятся сразу после выпуска из университета. Выбирая это направление деятельности, важно понимать, что карьерный рост не будет стремительным. Зато специалисту гарантирована интересная работа в солидных и стабильных компаниях.</p><p>Так как найти на рынке готового профессионала с правильным бэкграундом и нужной экспертизой не так уж легко, интеграторы предпочитают развивать специалистов внутри своих компаний. Они активно вкладывают в обучение и всеми доступными средствами стараются удерживать талантливых и мотивированных сотрудников как можно дольше, чтобы эти инвестиции не пропали зря. Именно поэтому в известных ИТ-интеграторах обычно очень низкая текучка кадров.</p><p>Опыт Rubytech показывает: чаще всего системными архитекторами становятся сотрудники, которые начинали свою карьеру с должности эксперта-проектировщика. В некоторых компаниях их называют техническими писателями. И это не случайно, ведь проектирование документации — одна из ключевых функций специалистов по разработке системной архитектуры. Чтобы успешно реализовать масштабный проект, требуется его детальное описание. Оно включает обоснование выбора архитектуры каждого из предлагаемых решений и подробные инструкции по их эксплуатации для администраторов со стороны заказчика. Проектировщик занимается проработкой всех этих аспектов на уровне документации.</p><p>Параллельно он развивается по индивидуальной траектории — прокачивает свои знания и навыки под руководством старших наставников, проходит внутреннее и внешнее обучение и сертификацию у вендоров. Постепенно берет на себя все более сложные, творческие и ответственные задачи — из младшего эксперта становится старшим, затем — ведущим. Следующая ступень — уже системный архитектор.</p><h2>Как долго ждать карьерного роста</h2><p>Темпы роста у всех очень разные. Случается, что молодые специалисты с большими амбициями, резво стартовав, перестают развиваться на середине пути. А бывает — ровно наоборот. Например, в Департаменте системной архитектуры Rubytech, которым я руковожу, есть сотрудница, которая поначалу не демонстрировала впечатляющих карьерных результатов, просто аккуратно выполняла свою работу. А недавно — попала на большой и сложный проект, проявила самостоятельность, приняла несколько грамотных управленческих решений, показала высокий профессионализм и после медленного разгона совершила качественный профессиональный скачок.</p><p>В целом именно так и бывает — специалисты быстро вырастают на непростых проектах, которые обычно длятся не меньше года. За это время можно научиться многому и ярко проявить себя. Поэтому я советую молодым сотрудникам быть инициативными и искать для себя вызовы. Если на проекте легко и комфортно — значит, вы его переросли.</p><h2>Что нужно знать и уметь на входе в профессию</h2><p>Чтобы попасть на работу проектировщиком, нужно иметь представление о базовых понятиях и принципах построения ИТ-инфраструктуры, знать основных вендоров и разбираться в их продуктах. Необходимо иметь законченное техническое образование. Как и на любой должности в сфере технологий, нужно уметь читать и составлять документацию на русском на английском языке.</p><p>Но главное — иметь непреодолимое желание трудиться в сфере ИТ, способность быстро обучаться и усваивать огромные массивы информации. Внимание к деталям — еще один немаловажный фактор, без которого невозможна успешная работа в должности проектировщика.</p><h2>Коротко о главном</h2><ul><li>Системный архитектор — значимый специалист в структуре системного интегратора, без которого невозможно построить ни одну ИТ-инфраструктуру.</li><li>Чтобы работать системным архитектором, необходимо иметь большой опыт и глубокие познания в ИТ.</li><li>Помимо этого, важно обладать отличными коммуникативными навыками, уметь проводить презентации и руководить командой.</li><li>До системного архитектора можно дорасти через позицию проектировщика, эксперта или инженера.</li><li>Быстрый рост обычно происходит на самых сложных проектах.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Почему не hadoop: создаём свое решение на node + mongo + lxd</title>
      <link>https://tproger.ru/articles/highload-mongo-node-lxd</link>
      <comments>https://tproger.ru/articles/highload-mongo-node-lxd?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/highload-mongo-node-lxd</guid>
      <description><![CDATA[<p>Клиент рассчитывал применить Hadoop для отчётов объёмом более 100 тысяч строк, каждый из которых создавался от 12 до 24 часов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/highload-mongo-node-lxd">Почему не hadoop: создаём свое решение на node + mongo + lxd</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Высокие нагрузки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 19 Aug 2020 07:50:29 GMT</pubDate>
      <content:encoded><![CDATA[<p>Иногда к нам приходят клиенты с уже определенным стеком технологий для своего проекта. Так было и в этот раз. Клиент хотел проект по обработке больших объемов данных, твердо уверенный, что Hadoop это серебряная пуля.</p><p>Disclaimer В данной статье речь пойдет о проекте которому на текущий момент около трех лет. Некоторые решения, в том числе и выбор технологий обуславливался временем разработки.</p><p>Клиенту нужно было регулярно проводить электронную инвентаризацию оборудования, каждый отчет по такой инвентаризации содержал более 100k строк. Клиент видел лишь одну проблему — отчеты генерировались слишком долго (в районе 12 часов – 24 часа на каждый отчет). По мнению клиента, эта проблема вытекала из следующих факторов:</p><ul><li>Используется неподходящая СУБД. MongoDB не может в много данных и их обсчет. В результате этого, сложный анализ этих данных занимал слишком много времени и памяти.</li><li>Выходной формат отчетов — excel (xlsx). Для этого использовались скрипты на Node.js, каждый из которых выдавал отчет в своем виде. При построении таких отчетов, оперативная память заполнялась (~8Гб) и любое обращение к базе данных —  MongoDB просто падало с ошибкой.</li></ul><p>Задача: Поднять Hadoop кластер, настроить миграцию данных из БД Клиента и обработку этих данных средствами Hadoop.</p><p>На тот момент времени, у Hadoop была очень слабая поддержка, неразвитая инфраструктура, длительное время не было обновлений и неполная документация. Это всегда накладывало свой отпечаток на время и стоимость проекта.</p><p>Примечание На текущий момент, Hadoop имеет более дружелюбную инфраструктуру, благодаря docker и k8s.</p><p>При анализе необходимых действий для того, чтобы перенести все необходимые данные в Hadoop, мы выявили что мало того, что данные не имеют четкой структуры, так и в некоторых местах существенно различаются по типу и свойствам (спасибо Mongo за гипергибкость, которая попадает в руки не заботящихся о проектировании данных людей). Ну и в чем проблема, спросите вы. А проблема заключается в том, что Hadoop это SQL-based система. Т.е. все интерфейсы взаимодействия с данными идут через структурированный SQL. И у нас нет возможности мигрировать в эту систему данные без схемы, типов и прочего.</p><p>Для того чтобы нормально проработать схемы данных, потребовалось бы очень много времени. А клиенту нужно было как можно скорее. Поэтому на очередной сессии мозгового штурма, мы выделили какие есть проблемы на наш взгляд и придумали как можно их решить.</p><p>После глубокого исследования, мы составили свой список причин, почему у них проблема с производительностью:</p><ul><li>Нет четкой схемы данных в БД. Это приводит к тому, что агрегации должны выполнять больше количество действий для расчета результата.</li><li>В текущей БД свалка данных. При разработке системы, которая генерировала эти данные, формат меняли несколько раз, при этом сама БД никогда не очищалась. В результате чего, операции обсчета документов выполнялись даже над теми документами, которые не соответствуют нужной схеме.</li><li>Часть логики по обработке данных было перенесено на сторону Node.js.</li><li>Отчеты генерировались в памяти, и после полной генерации сохраняли это все в excel файл (естественно, когда данных много, оперативной памяти не хватает и вся система рушится).</li></ul><p>А также придумали как эти проблемы решить.</p><ul><li>За архитектурную модель мы взяли принцип по которому работают все распределенные системы, в том числе Hadoop. (Есть один мастер и несколько агентов.)</li><li>Мастер управляет нагрузкой и взаимодействием с внешним миром.</li><li>Каждый из агентов выполняет свою задачу по обработке данных.</li><li>Все данные нужно разделить на несколько взаимонезависимых scope, основываясь на локальности данных при их использовании, для того чтобы можно было максимально распараллеливать задачи по обработке этих данных.</li><li>Каждый scope хранить в отдельной БД, для снижения нагрузки на БД.</li><li>Использовать документоориентированную базу данных, чтобы упростить переход, но прописать вариации схем данных.</li><li>Для отказоустойчивости и предотвращения потери данных, каждая БД должна быть представлена replica set как минимум на 3-х разных физических машинах.</li><li>Мы понимали, что система будет большой и сложной для поддержки. Поэтому нам необходим был инструмент, который бы автоматизировал большинство действий с системой за нас.</li></ul><p>Общую схему как выглядела система вы можете увидеть на следующем рисунке.</p><figure><img src="https://media.tproger.ru/uploads/2020/08/HA2.png" alt="" /></figure><p>Изображение компании Dunice</p><p>Superhost и каждый хост это отдельная физическая машина, со своими ресурсами и настроенным Raid 0 (или Raid 5).</p><p>Superhost (он же мастер) состоит из:</p><ul><li>Основного сервиса (Main Web Server, MWS), который обрабатывает запросы от пользователей по специальным ендпоинтам, мониторит состояние всех хостов, запускает и мониторит состояние миграций данных из внешней БД. Извне, мы в любой момент можем увидеть статистику по использованию ресурсов и текущему состоянию системы.</li><li>Metadata service — это сервис хранилище данных о состоянии кластера (какой scope на какой реплике, какой агент с этой репликой лучше работает, когда была последняя миграция для каждого scope и т.д.). Взаимодействовал с этим сервисов только MWS и CLI</li><li>CLI — это терминальный интерфейс для развертывания и управлением кластером. Назван в честь самой системы.</li><li>Планировщик задач. Этот сервис отвечал за запуск миграций в нужное время.</li></ul><p>Каждый из host N состоит из:</p><ul><li>Один или несколько контейнеров с агент-сервисом на каждом хосте. Нужен, для того чтобы взаимодействовать с базами данных, выполнять миграцию, запускать агрегацию, в режиме потока строить отчет нужного формата и отправлять на MWS.</li><li>Контейнер с базой данных, которая реплицирована на несколько хостов (min 3).<br />В базе данных хранились мигрированные данные из основной БД клиента, но разбиты по scope на разные группы реплик. При инициации миграции, данные брались с некоторым условием, так чтобы в одной группе реплик были данные только для одного scope.<br />Это позволило нам сократить нагрузку на обработку данных и увеличить скорость выдачи отчета. После окончания миграции, мы запускали процесс агрегации (предвычисления). Эта операция предназначена для того, чтобы посчитать все нужные данные для отчетов и положить их в отдельную таблицу. В итоге при построении отчета, мы просто берем данные с фильтрацией из этой таблицы-кеша (получается по сути индекс). Данное решение позволило нам генерировать отчеты на 30 колонок и 200к+ строк за пару минут.<br />Когда приходило время новой миграции, мы таблицу с закешироваными данными переименовывали и работали с ней. После того как миграция окончилась и появилась таблица с новыми закешироваными данными, мы удаляли старую и работали с новыми данными.</li></ul><p>А для того чтобы как то удобно говорить об этой системе, мы решили дать ей имя MoNoRe — по первым слогам ключевых технологий (Mongo, Node, Redis).</p><p>Принцип работы при миграции данных:</p><ol><li>Посылаем запрос на адрес &lt;superhost&gt;/migration/&lt;scopeId&gt;/start</li><li>При первом обращении к MWS с запросом на миграцию будет выбран наименее нагруженный хост и на нем создается первый «data» контейнер с настройками репликации. После этого по этому же принципу будут выбрано некоторое количество хостов для создания «data» контейнеров в количестве указанном в файле inventory.ini как replicaCount. Как только все необходимые контейнеры будут готовы, система инициирует создание replicaSet, дожидается окончания этого процесса и возвращает MWS IP адреса всех участников.</li><li>MWS создает расписание на ежедневную миграцию для указанного scope</li><li>Записывает данные о репликах в Redis</li><li>Запрашивает наименее нагруженный «compute» и отправляет ему команду на запуск процесса миграции для указанной фирмы.</li><li>Агент, получив команду на миграцию соединяется с replicaSet, создает(если она отсутствует) новую базу и начинается процесс миграции.</li><li>Прогресс миграции периодически отправляется в MWS и сохраняется в Redis. В любой момент, извне, мы можем запросить состояние миграции нужного scope у MWS &lt;superhost&gt;/migration/&lt;scopeId&gt;/status.</li><li>Когда все данные успешно перенесены, на суперхост будет отправлено сообщение об успешном выполнении миграции.</li><li>Запускается механизм агрегации данных для построения будущих отчетов в коллекцию report_rows, при этом, если на данный момент такая коллекцию существует — она будет переименована в report_rows_cache, а запись будет осуществляться в новую report_rows.</li><li>Прогресс агрегации периодически отправляется в MWS и сохраняется в Redis. В любой момент, извне, мы можем запросить состояние агрегации нужного scope у MWS &lt;superhost&gt;/migration/&lt;scopeId&gt;/status.</li><li>Когда коллекция report_rows будет готова, на суперхост будет отправлено сообщение об успешном выполнении агрегации.</li><li>Записывает данные о времени последней миграции в Redis</li></ol><p>При последующих запросах на миграцию суперхост берет всю необходимую информацию из Redis, запрашивает наименее нагруженный «compute» и отправляет ему команду на запуск процесса миграции для указанного scope.</p><p>Принцип работы при получении отчета:</p><ol><li>Посылаем запрос на адрес &lt;superhost&gt;/report/&lt;reportId&gt;?additionalParams="</li><li>При первом обращении к MWS с запросом на миграцию будет выбран наименее нагруженный хост и на нем создается первый «data» контейнер с настройками репликации. После этого по этому же принципу будут выбрано некоторое количество хостов для создания «data» контейнеров в количестве указанном в файле inventory.ini как replicaCount. Как только все необходимые контейнеры будут готовы, система инициирует создание replicaSet, дожидается окончания этого процесса и возвращает MWS IP адреса всех участников.</li></ol><p>Как видно из схемы для реализации нашей системы мы решили взять следующие инструменты:</p><ol><li>Node.js (with express) — Использовался для создания HTTP(S) серверов MWS и CS</li><li>MongoDB (with replication) — Так как у команды клиента есть опыт работы с MongoDB, при этом она была достаточно мощной для обработки больших массивов данных за счет Agregation Pipeline, было принято решение ее и использовать как основное хранилище данных.</li><li>ПримечаниеLXD / LXC (Linux Containers) — Система виртуализации, реализующая концепт контейнеров. Использовался для добавления слоя абстракции между элементами системы и серверами на которых происходит разворачивание.  В настоящее время имеет смысл использовать Docker + Docker Swarm)</li><li>SSH – Использовался как транспорт для управления между хостами</li><li>Node.js (with Yargs and Ora) — Использовался при построении CLI.</li><li>Cron(Linux) — выполнял роль планировщика задач.</li><li>PM2 — Использовался для повышения отказоустойчивости Node-Express сервисов.</li><li>Redis — Выполнял роль Metadata service.</li><li>Nginx — На master машине выполняет роль прокси сервера, для взаимосвязи с внешним миром. На каждом хосте выполняет роль прокси сервера для общения между MWS и агентом.</li></ol><h2>Развертывание и управление большой системой = боль?</h2><p>Система на первый взгляд выглядит непросто. Мы понимали что управлять всем этим руками будет не удобно и будет вызывать много вопросов и проблем у заказчика. Чтобы руками развернуть такую систему необходимо очень четко понимать все ее модули, взаимодействие между ними и потратить пару часов. А это очень не устраивало нас как 0-пользователей системы.</p><p>Чтобы решить эту проблему, мы создали CLI c набором команд, позволяющий привести систему в рабочее состояние за 5 минут. Для этого необходимо просто заполнить inventory.ini (см. рис 2) файл с необходимыми данными и запустить команду monore setup. Система сама зайдет на все указанные хосты системы, развернет там необходимые модули, создаст нужные файлы с настройками(nginx, mongo и так далее) и переведет систему в рабочее состояние.</p><figure><img src="https://media.tproger.ru/uploads/2020/08/image2-2.png" alt="" /></figure><p>Изображение компании Dunice</p><figure><img src="https://media.tproger.ru/uploads/2020/08/image4-1.png" alt="" /></figure><p>Изображение компании Dunice</p><p>На первом демо, у тех. директора со стороны заказчика был крайне негативный настрой (оказалось, что до самой демонстрации он не был подключен в наш разговор, и решение о замене Hadoop на наше решение Клиент принимал самостоятельно). Однако, по мере демонстрации возможностей и характеристик системы, интерес возрастал, негодование сменилось удовлетворением, а глаза начали блестеть.</p><p>Это было тяжелое демо с нашей стороны, но все участники были довольны достигнутым, оставалось лишь одно НО…. Уровень отказоустойчивости который у нас был на текущий момент, оказался недостаточным для инициирования процесса приёма-передачи продукта. У нас появилось новое требование, нужно чтобы с системой могло произойти все что угодно (отключили от сети или вышло из строя железо хоста или супер хоста, временные проблемы с доступностью хоста), но система должна продолжать работу, и при этом нормально реагировать на возращении хоста (предотвращение коллизий и несогласованности данных).</p><p>Таких жестких требований к жизнеспособности системы не мог поддержать и Hadoop. Однако мы не стали спорить с Клиентом, и принялись думать как нам это реализовать.</p><h2>Тот самый момент, когда грамотная архитектура решает проблемы за тебя</h2><p>На самом деле, часть этих требований у нас уже были покрыты за счет правильно подобранной архитектуры и программных решений. Например, вопросом коллизий и согласованности данных полностью занималась MongoDB с настроенным Replica Set. А за счет того, что мы храним некоторые метаданные о кластере, потеря одного хоста вообще не является проблемой.</p><p>Однако для достижения поставленных целей этого недостаточно. Все еще могут быть проблемы, если откажет Superhost или больше половины host.</p><p>Решить эту проблему мы смогли с помощью более крупной кластеризации за счет LXD Cluster (теперь контейнеры на всех хостах находились как бы на одном хосте, видели друг друга и могли спокойно общаться, однако появилось требование, чтобы хосты (в том числе и суперхост) должны быть в одной подсети (16 или 24 ранга)), обновлении логики работы MWS (упростился механизм выборки и взаимодействия с агентами, появилась логика регенерации) и CLI (усложнилась логика разворачивания системы, однако упростился механизм сбора статистики с каждого модуля, появилась логика регенерации), и обновление количества и качества хранимых метаданных о системе.</p><p>Шаг с кластеризацией, помог нам более гибко управлять кластером из любого места кластера. Т.е. по факту, любой хост мог стать супер хостом.</p><p>Общий алгоритм работы:</p><ul><li>Если теряется связь с хостом, то админу отправляется оповещение со всей необходимой информацией о хосте. А так же, о том что в систему имеет смысл добавить еще хостов, для поддержания высокой отказоустойчивости. При добавлении нового хоста, через CLI, система в автоматическом режиме начинает его полноценное использование.</li><li>Если выходит из строя суперхост, то устраивается кворум, и один из хостов становится суперхостом. Инициируется процесс регенерации. Устанавливается Redis, MWS, CLI. Происходит сбор информации о кластере (за счет того, что в каждой реплике БД есть минимальное количество метаданных о самой реплике, о данных которые в ней хранятся и о последней миграции). В итоге через примерно 30 секунд на кластере из 20 машин, система снова становится обрабатывать запросы извне. Так же отправляется письмо администратору, с информацией о произошедшем и результатом.</li></ul><figure><img src="https://media.tproger.ru/uploads/2020/08/image1-1.png" alt="" /></figure><p>Изображение компании Dunice</p><p>Итогом данного проекта стало успешное внедрение сначала в тестовый контур, а в последствии и в продакшен. Итоговые характеристики системы при построении отчета:</p><ol><li>Оперативная память не поднимается выше 500мб при формировании отчета ~200к строк из таблицы с &gt; 1млн записей</li><li>Время получения отчета ~60 секунд (для ~200к строк).</li></ol><p>А для нас, результатом был ценнейший опыт построения Enterprise систем. Мы еще раз убедились, что правильно подобрать инструмент под задачу, это всего лишь полдела, нужно еще уметь правильно готовить с помощью этого инструмента (ну, и грамотная архитектура дает огромную свободу развития и масштабирования приложения).</p><p>Изначально, клиент был недоволен результатом, который получился у его команды. Мы же, взяли те же инструменты, добавили немного новых, правильно определили архитектуру и применили весь наш опыт в разработке Enterprise систем.</p>]]></content:encoded>
    </item>
    <item>
      <title>Нужны ли вашему проекту микросервисы? Вопросы, которые помогут разобраться</title>
      <link>https://tproger.ru/translations/you-dont-need-microservices</link>
      <comments>https://tproger.ru/translations/you-dont-need-microservices?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/you-dont-need-microservices</guid>
      <description><![CDATA[<p>Микросервисы кажутся серебряной пулей, но подходят не каждому проекту — о чём стоит подумать, прежде чем дробить свой монолит.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/you-dont-need-microservices">Нужны ли вашему проекту микросервисы? Вопросы, которые помогут разобраться</a>»</p>]]></description>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 16 Mar 2020 18:59:06 GMT</pubDate>
      <content:encoded><![CDATA[<p>Автор Алексей Морозов</p><p>На дворе 2020 год, и весь Интернет только и делает, что обсуждает бесчисленные достоинства микросервисов. Истории успеха правдивы, микросервисы хороши для своих задач. Вот только, как и любая модная идея, они кажутся «серебряной пулей», решающей все проблемы на свете и подходящей для абсолютно любого проекта. Это не совсем так, и прежде чем дробить свой монолитный проект на микросервисы, надо задать себе несколько вопросов и убедиться, что они вам нужны.</p><h2>У вас действительно огромный монолит?</h2><p>Микросервисы всегда противопоставляют «огромным монолитным системам», и ключевое слово тут «огромная». Прежде всего нужно понять, так ли ваша система велика, что её имеет смысл дробить на части. Микросервисы по определению — набор отдельных сервисов, каждый из которых выполняет свою задачу. В идеале, каждый из них должен быть независимым от остальных.</p><p>Это означает, что у микросервиса есть предел сложности (и в количестве строк, и в вычислительных ресурсах), ниже которого он не будет работать. Грубо говоря, модуль в составе монолита может ограничиться несколькими классами, а микросервис (каждый!) обязан предоставлять API, получать данные из сети, обеспечивать их валидацию и хранение, самостоятельно вести логи и т. д. Из-за этого стоимость разработки для микросервисов и монолитов выглядит примерно так:</p><figure><img src="https://media.tproger.ru/uploads/2020/03/Vam-ne-nuzhny-mikroservisy-1.jpg" alt="" /></figure><p>Есть такой размер системы, выше которого микросервисы начинают окупаться. Если вы его ещё не прошли — они вам не нужны. Да, в будущем проект вырастет. Но <a href="https://tproger.ru/articles/deploj-monolita-na-net-framework-4-8-i-oracle-s-pomoshhju-ansible/">хорошо спроектированный монолит</a> можно будет раздробить тогда, когда (и если) это будет иметь смысл.</p><h2>Вам действительно нужно масштабировать отдельные компоненты?</h2><p>Допустим, нужно разработать систему кадрового учёта для организации на 10 000 человек. Поскольку микросервисы — это круто, архитектура будет примерно такой:</p><figure><img src="https://media.tproger.ru/uploads/2020/03/Vam-ne-nuzhny-mikroservisy-2.jpg" alt="" /></figure><p>Пример несколько издевательский, но серьёзно: зачем нужно приложение для учёта зарплаты, отдельное от приложения для учёта сотрудников? Они всё равно будут работать на одних данных и расти параллельно друг другу.</p><p>Одно из достоинств микросервисов — в возможности масштабировать отдельные компоненты по мере необходимости. В принципе это полезно, но есть ли такая необходимость конкретно в вашем проекте?</p><h2>Возникают ли транзакции, в которых участвует несколько сервисов?</h2><p>Такие транзакции — одна из самых больших сложностей в микросервисной архитектуре. Они возникают, если некое событие, единое с точки зрения бизнес-логики, должно быть обработано одновременно сразу несколькими сервисами. Или, того хуже, если при обработке этого события сервисы должны несколько раз обменяться информацией. Разрешение таких событий требует, чтобы операции на всех сервисах проходили в определённом порядке, то есть при определённых условиях сервисы должны блокироваться. А значит, добро пожаловать в прекрасный мир дедлоков, состояний гонки и трудновоспроизводимых багов.</p><p>REST-сервисы по определению не должны иметь внутреннего состояния, так что им лучше не вступать в сложные операции на несколько сервисов. В мире высокой нагрузки двухфазные коммиты (<a href="https://en.wikipedia.org/wiki/Two-phase_commit_protocol">2PC</a>) — ненужное зло, а паттерн <a href="https://microservices.io/patterns/data/saga.html">SAGA</a> только добавит сложности.</p><h2>Часто ли данные передаются между сервисами?</h2><p>В классической монолитной архитектуре микросервисам соответствуют модули в составе приложения. Они все находятся в памяти одной и той же физической машины, поэтому передача данных практически ничего не стоит. Как только проект разделяется на микросервисы — данные начинают передаваться через сеть.</p><p>Есть множество прекрасно работающих решений для обмена данными между микросервисами, но для сетевого соединения задержка неизбежно измеряется не в наносекундах, а в микросекундах. Если, например, три сервиса общаются друг с другом по сети, а каждый вызов занимает 100 мс (что для высоконагруженной системы вполне реально) — то треть секунды уходит на одну только передачу данных, безо всяких вычислений. Для систем, обрабатывающих данные в реальном времени, такие потери недопустимы. Просто представьте себе лаги в хирургическом роботе или софте авиадиспетчера!</p><h2>Что ещё нужно учитывать?</h2><ul><li><b>Дополнительная сложность. Да, вся идея микросервисов в том, что каждый из них в отдельности становится проще и понятнее. Но систему в целом сложнее деплоить и поддерживать.</b></li><li><b>Дополнительная стоимость</b>. Монолитная система может хоститься на единственной виртуальной машине или в контейнере, а микросервисы (в идеале) хостятся по отдельности. Да, виртуалки будут не такими большими, но их будет много, и скорее всего это будет дороже.</li><li><b>DevOps. Это можно считать и за достоинство, и за недостаток, но с микросервисами вам однозначно понадобится DevOps-команда. С одной стороны — её в любом случае неплохо иметь, а с другой — она вам прямо сейчас по карману?</b></li><li>Неизбежная связность. Некоторые задачи в вашем проекте наверняка плотно связаны друг с другом по своей природе (тот же учёт сотрудников и учёт зарплат, например). Любая попытка их разделить ради соответствия архитектуре скорее всего закончится печально.</li><li>Неопытная команда. Это, конечно, проблема для любого проекта. Но в погоне за абстрактным идеалом «микросервисов» можно получить чудовищно плохую архитектуру. Когда сервисы перестанут работать, если их деплоить в неправильном порядке, или когда падение одного сервиса обрушит все остальные, будет уже слишком поздно.</li><li>Сложность тестирования. Для монолита тесты обычно запускаются одной командой, а для тестирования набора взаимосвязанных сервисов нужна отдельная система.</li><li>Совместимость данных. Согласование форматов данных в пределах одной команды — это совсем не то же самое, что согласование между несколькими командами, которые к тому же могут находиться на разных концах планеты и/или программировать на разных языках. На обсуждение спецификаций неизбежно потребуются дополнительные время и силы.</li><li>Поддержка старого кода. Давайте честно: большинство систем не создаётся с нуля. Новая архитектура — это хорошо, но она усложняет взаимодействие с уже имеющимся кодом, и неплохо бы убедиться, что ваш новенький микросервисный фреймворк будет нормально работать с приложением, которое уже 20 лет как крутится на <a href="https://en.wikipedia.org/wiki/IBM_AIX">IBM AIX</a>.</li><li>Отладка и мониторинг. Отдельные системы — это в том числе и отдельные логи. Найти ошибку где-то в пяти раздельных лог-файлах сложнее, чем в одном.</li></ul><p>Значит ли это, что не стоит пользоваться микросервисами? Нет. История Netflix вдохновила многих, и вполне заслуженно. <a href="https://eng.uber.com/service-oriented-architecture/">Uber</a>, <a href="https://developers.soundcloud.com/blog/building-products-at-soundcloud-part-2-breaking-the-monolith">SoundCloud</a> и<a href="http://highscalability.com/amazon-architecture"> Amazon</a> используют микросервисы и делают с их помощью вещи, которые раньше казались невозможными. Но это не означает, что микросервисы — идеальная архитектура, подходящая для всех проектов.</p><p>Многим программистам хочется использовать все крутые идеи, которые они увидели. Но тем и отличаются хорошие программисты, что они понимают разницу между «крутыми идеями» и «идеями, подходящими в данном конкретном случае».</p>]]></content:encoded>
    </item>
    <item>
      <title>Как разрабатывается умный поиск — нюансы и сложности</title>
      <link>https://tproger.ru/articles/how-smart-search-is-created</link>
      <comments>https://tproger.ru/articles/how-smart-search-is-created?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/how-smart-search-is-created</guid>
      <description><![CDATA[<p>Архитектура высоконагруженного сервиса Searchanise: умный поиск с подсказками и исправлением ошибок, навигация, рекомендации и рост конверсии.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/how-smart-search-is-created">Как разрабатывается умный поиск — нюансы и сложности</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Высокие нагрузки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 15 Feb 2020 12:57:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Всем привет! Меня зовут Дима, я занимаюсь разработкой Searchanise — это облачный сервис для поиска товаров и повышения конверсии внутри интернет-магазинов.</p><p>Я расскажу про архитектуру быстрого высоконагруженного сервиса, который включает: умный поиск, навигацию, рекомендательную систему и инструменты повышения конверсии в интернет-магазине.</p><h2>Как развивалась функциональность сервиса</h2><p>Ядро сервиса — это поисковый движок, который угадывает запрос по первым введённым символам, исправляя ошибки и предлагая наиболее актуальные подсказки. Поиск сопровождается виджетом, где отображаются продукты с изображениями, ценой, остатком на складе, рейтингом, а также релевантные поисковому запросу контентные страницы, категории и подсказки.</p><p>Новые опции появлялись по мере сотрудничества с платформами. Когда мы приходили на новую платформу, то понимали, какой функциональности не хватает. Так появилась страница результатов поиска, фильтры, рекомендательные блоки, мерчендайзинг, промоинструменты, аналитика. Потом мы поняли, что клиентам нужны рекомендации и начали развиваться в этом направлении. Чтобы рекомендации хорошо работали, в планах развивать персонализацию поиска и внедрять машинное обучение.</p><h2>Архитектура</h2><p>Главное в системе — это архитектура. В нашем случае — доработка архитектуры с целью повышения скорости.</p><p>Хостимся на 10 железных серверах. Используем виртуализацию KVM, создали свое маленькое облако, в котором у нас больше 50 виртуалок. На одном железе — примерно 6 серверов, каждый из которых отвечает за что-то своё: какой-то принимает запросы, какой-то только хранит статистику, отдельный сервер для админки, отдельный сервер для индексации. Больше всего поисковых серверов — 30, так как Sphinx для организации быстрого поиска требуется много памяти.</p><p>Когда клиент устанавливает модуль, он регистрируется на сервере и начинает отправлять данные для поиска. Все эти данные попадают в очередь, а из неё — в базы данных. После того как импорт данных закончен, команда на индексацию попадёт в очередь индексации. Обработчики очереди API у нас расположены на 10 серверах, а обработчики индексации — на 19 серверах.</p><p>Очереди сделаны очень необычно: мы реализовали их сами в Redis с помощью LUA скриптов и встроенных в Redis типов данных. Получилось хорошо: мы смогли часть логики переложить в очередь и получили очереди, которые распараллеливаются по нужным нам условиям. Ещё наши очереди умеют повторять ошибочные запросы только фиксированное число раз и при падении обработчика данные не теряются.</p><p>Для каждой регистрации создается своя база — на данный момент у нас их 10 000. Это нестандартное решение, у которого есть огромное преимущество — мы реально изолируем базы. Удалить базу гораздо проще, чем вычищать информацию из таблиц. Помимо этого, решается еще одна проблема: при добавлении или частой работе с обновлениями базы, индекс тормозит. Когда базы отделены, не возникает проблем с шардированием. Просто раскладываем определенное число баз на каждый сервер Mysql.</p><p>Приложение для Shopify написано на Erlang, который оказался очень быстр и удобен для задач параллельного программирования: с его помощью не проблема поддерживать тысячи магазинов, делая по несколько миллионов запросов к API. У Shopify очень развитое API, с которым можно делать практически всё. С помощью API мы сделали индексацию, следим за обновлениями, статусами магазинов и т.д. Приложение изолировано для того, чтобы соблюсти общую монолитную структуру и иметь единый API для всех платформ.</p><p>Со всех серверов собираем метрики с помощью Zabbix. Это очень удобно — в каждый момент знать что происходит с сервером и насколько он загружен. Также в Zabbix настроены триггеры на разные «внештатные» ситуации — если что-то пойдет не так, придёт уведомление в Slack и на почту.</p><h2>Работа со Sphinx</h2><p>Благодаря Sphinx и тому, что мы много времени потратили на оптимизацию, скорость поиска Searchanise во много раз выше конкурирующих предложений — мы легко поддерживаем 500 000 продуктов в поиске. И эта возможность дана Sphinx. Ежедневно мы обрабатываем 15 млн поисковых запросов.</p><p>Но мы знаем и о проблемах Sphinx, и многие уже научились обходить. Но осталась одна: Sphinx периодически падает и с этим пока ничего не получается поделать. Поэтому мы один индекс держим на двух серверах: если Sphinx упадёт на одном, второй будет обслуживать запросы.</p><h2>Тестирование</h2><p>Мы не используем unit-тестирование. Когда система быстро развивается, поддержка unit-тестов в актуальном состоянии сильно тормозит процесс разработки.</p><p>Все метрики проверяем самописными короткими тестами; используем функциональные тесты для API. API у нас фиксировано и тесты не приходится часто менять.Считаем, что функциональное тестирование эффективнее регулярной правки unit-тестов под меняющиеся требования.</p><h2>Виджеты</h2><p>На некоторых платформах результаты поиска показываются в дизайне платформы. На других платформах (например Shopify) поиск отображается в JS-виджетах. Всё это управляется нашей встраиваемой админкой, которая тоже JS-виджет.</p><p>У нас огромное количество настроек для системы виджетов. Все настройки обновляются практически в режиме реального времени (в течение 10–20 секунд оказываются у клиента). У конкурентов иногда приходится ждать 10–15 минут.</p><p>Файлы виджетов мы храним на Amazon S3, но раздаем не напрямую, а используем CDN. Мы используем KeyCDN: недорого, хорошо работает, изменения быстро актуализируются, IP-адреса не заблокированы ни в одной стране.</p><h2>Вывод</h2><p>За счёт использования Sphinx мы построили сервис поиска для множества клиентов и обеспечили себестоимость практически в 10 раз ниже, чем у конкурентов. Но Sphinx из коробки очень неудобен. Неудобно его использовать со множеством индексов и организовать на его основе распределенный сервис. Нам пришлось преодолеть много трудностей и придумать много уникальных решений для налаживания процесса индексации, распараллеливания и faleover.</p>]]></content:encoded>
    </item>
    <item>
      <title>Качественная архитектура ПО на примере концепции Linux «всё есть файл»</title>
      <link>https://tproger.ru/translations/good-software-code-architecture</link>
      <comments>https://tproger.ru/translations/good-software-code-architecture?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Сергей Штукатуров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/good-software-code-architecture</guid>
      <description><![CDATA[<p>Концепция Linux «всё — файл» помогает создавать качественную архитектуру. На примере FFmpeg показана организация кодовой базы на C с расширяемостью.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/good-software-code-architecture">Качественная архитектура ПО на примере концепции Linux «всё есть файл»</a>»</p>]]></description>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Основные принципы программирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 03 Oct 2019 14:31:01 GMT</pubDate>
      <content:encoded><![CDATA[<p>В статье показано, как в Linux/FFmpeg организована кодовая база на C с учётом расширяемости, которая работает так, будто в C есть полиморфизм. Вы увидите, как концепция Linux «всё — файл» работает на уровне исходного кода, а также как FFmpeg позволяет быстро и легко добавлять поддержку новых форматов и кодеков.</p><figure><img src="https://media.tproger.ru/uploads/2019/10/diagram_components.jpg" alt="" /></figure><h2>Качественный дизайн ПО — введение</h2><p>В процессе работы над кодом программисты <a href="https://martinfowler.com/articles/is-quality-worth-cost.html">регулярно сталкиваются</a> с тем, что качественный дизайн кода <a href="https://www.martinfowler.com/bliki/Yagni.html">окупается впоследствии</a> при усложнении продукта. Для создания полезного и легко поддерживаемого в долгосрочной перспективе ПО разработчики подбирают определённые шаблоны и объединяют их в абстракции, и похоже, что разработчики Linux и FFmpeg поступили именно так.</p><p>При разработке программ создаются структуры данных и определяются их зависимости и поведение. То, как они построены и связаны между собой, можно рассматривать как дизайн/архитектуру ПО.</p><p>Предположим, что мы разрабатываем фреймворк для обработки видео- и аудиофайлов. Кодеки <a href="https://aomedia.org/">AV1</a>, H264, HEVC и AAC производят некоторые идентичные операции с данными, и если мы разработаем некоторую обобщённую абстракцию, включающую эти операции, мы сможем использовать эту абстракцию вместо того, чтобы реализовывать конкретную идею, заложенную в каждом отдельном кодеке.</p><p>Ещё один хороший приём — использовать слабо связанные компоненты, чётко определив их функции.</p><h2>Ruby</h2><p>Возможно, эти концепции проще понять на практике. Сделаем примерный набросок фреймворка для обработки потоковых медиаданных, использующего несколько разных кодеков.</p><p>Этот код на <a href="https://www.ruby-lang.org/en/">Ruby</a> отражает одну из описанных выше концепций. Без конкретизации в коде предполагается, что каждый кодек реализует функции encode и decode. Поскольку Ruby — язык с динамической типизацией, любой класс может иметь реализацию этих двух операций и работать как кодек.</p><p>Такой дизайн кода можно назвать хорошим, поскольку если нам потребуется добавить новый кодек, нужно только включить его реализацию в список. Разумеется, список можно сделать и динамическим. Смысл примера в том, что такой код легко расширять и поддерживать, поскольку компоненты слабо связаны между собой и каждый из них делает только то, что должен.</p><p>Фреймворк Ruby on Rails подталкивает к определённым способам организации кода, реализуя архитектуру «Модель-Представление-Контроллер» (MVC).</p><h2>Go</h2><p>Обращаясь к языкам со статической типизацией, таким как <a href="https://golang.org/">Go</a>, нам придётся быть более формальными, описывая требуемые типы, но мы всё равно можем создать код, аналогичный приведённому выше.</p><p>Тип <a href="https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c">interface в Go</a> намного мощнее аналогичной конструкции в Java, так как его определение никак не связано с реализацией, и наоборот. Можно даже присвоить каждому кодеку тип <a href="https://golang.org/pkg/io/#ReadWriter">ReadWriter</a> и использовать в таком виде.</p><h2>С</h2><p>На C тоже можно создать код с аналогичным поведением, но будут некоторые отличия.</p><p>Примечание Код создан по примеру <a href="https://www.bottomupcs.com/abstration.xhtml">размещённого</a> на сайте <a href="https://www.bottomupcs.com/">Computer Science from the Bottom Up</a>.</p><p>Сначала в обобщённой структуре мы определяем абстрактные операции (в данном случае функции). Затем мы наполняем их конкретным кодом, например кодером и декодером кодека av1.</p><p>Множество других языков поддерживают <a href="https://clojure.org/reference/protocols">сходные механизмы</a> распределения методов или функций, как если бы они придерживались некой конвенции. В результате ПО на уровне ОС достаточно уметь работать только с показанными высокоуровневыми абстракциями.</p><h2>Linux kernel и концепция «всё — файл»</h2><p>Концепция <a href="https://en.wikipedia.org/wiki/Everything_is_a_file">«всё — файл»</a> ОС Linux позволяет использовать один интерфейс для работы с любыми ресурсами системы. Например, Linux обрабатывает сетевые сокеты, особые файлы (такие как /proc/cpuinfo) и даже USB-устройства как файлы.</p><p>Этот подход облегчает разработку программ для ОС, поскольку мы можем использовать хорошо изученный набор операций для абстракции, названной «файлом». Вот как это работает:</p><p>Это возможно только потому, что концепция файла (структуры данных и операции) была разработана как один из главных способов взаимодействия подсистем.  Вот участок API-структуры file_operations:</p><p>Эта структура чётко определяет то, что мы подразумеваем под концепцией файла, и какое поведение мы от него ожидаем:</p><p>Здесь можно увидеть набор функций, реализующих это поведение, в <a href="https://github.com/torvalds/linux/blob/master/fs/ext4/dir.c#L653">файловой системе ext4</a>.</p><p>Даже файлы <a href="https://github.com/torvalds/linux/blob/master/fs/proc/cpuinfo.c#L19">cpuinfo proc</a> реализованы через эту абстракцию. Фактически, работая с файлами под Linux, вы используете виртуальную файловую систему (<a href="https://en.wikipedia.org/wiki/Virtual_file_system">VFS</a>), которая в свою очередь обращается к функциям абстракции.</p><figure><img src="https://media.tproger.ru/uploads/2019/10/pic2.jpg" alt="" /></figure><h2>FFmpeg — форматы</h2><p>Вот общая схема архитектуры <a href="https://github.com/leandromoreira/ffmpeg-libav-tutorial#ffmpeg-libav-architecture">процессов FFmpeg</a>, демонстрирующая, что внутренние компоненты связаны в основном через такие абстрактные концепции, как <a href="https://ffmpeg.org/doxygen/trunk/structAVCodec.html">AVCodec</a>, а не напрямую через конкретные кодеки.</p><figure><img src="https://media.tproger.ru/uploads/2019/09/pic3.png" alt="" /></figure><p>Для входящих файлов в FFmpeg создаётся структура <a href="https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/avformat.h#L640">AVInputFormat</a>, реализуемая любым форматом (видеоконтейнером), который требуется использовать. Файлы MKV также <a href="https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/matroskadec.c#L4229">заполняют эту структуру</a> своей реализацией, как и <a href="https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/mov.c#L7997">формат MP4 — своей</a>.</p><p>Такой дизайн позволяет легко интегрировать новые кодеки, форматы и протоколы. В мае 2019 года в FFmpeg был включён кодек <a href="https://code.videolan.org/videolan/dav1d">DAV1d</a> (аналог av1 с открытым исходным кодом), и, <a href="https://git.ffmpeg.org/gitweb/ffmpeg.git/commitdiff/beaa350e24167f4ff31275a6114693f5ce7cd409">изучив изменения в коде</a>, вы увидите, насколько безболезненно прошло внедрение. В итоге ему только требуется зарегистрироваться в качестве доступного кодека и придерживаться списка общих операций.</p><p>Безотносительно используемого нами языка мы всегда можем как минимум попытаться создать код со <a href="https://en.wikipedia.org/wiki/Loose_coupling">слабой зависимостью</a> и <a href="https://en.wikipedia.org/wiki/Cohesion_(computer_science)#High_cohesion">высокой согласованностью</a>. Именно эти два основных свойства позволят вам писать ПО, которое легко расширять и поддерживать.</p>]]></content:encoded>
    </item>
  </channel>
</rss>