<?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>JSON</title>
    <description>JSON для начинающих: от объяснения принципа работы до первых REST запросов. Лучшие туториалы по работе с JSON с нуля.</description>
    <link>https://tproger.ru/tag/json</link>
    <atom:link href="https://tproger.ru/tag/json/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sat, 03 Oct 2026 13:31:04 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>JSON</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>Как я написал AI-бота на aiogram 3 с помощью нейросетей, выжил при 2500+ пользователей и почему SQLite "всё ещё торт"</title>
      <link>https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p</link>
      <comments>https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Нуя]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p</guid>
      <description><![CDATA[<p>Технический кейс создания Telegram-бота Щёлк-ГДЗ (ИИ-репетитор по фото). Разбор архитектуры на Python (aiogram 3, aiosqlite), работы с API Gemini и решения проблем под нагрузкой 2500+ пользователей. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ya-napisal-ai-bota-na-aiogram-3-s-pomoshhyu-nejrosetej-vyzhil-p">Как я написал AI-бота на aiogram 3 с помощью нейросетей, выжил при 2500+ пользователей и почему SQLite "всё ещё торт"</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[Flash]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Маркетинг]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Пет-проект]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[VPS]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 26 Jul 2026 15:47:08 GMT</pubDate>
      <content:encoded><![CDATA[<p>На дворе 2026 год. Очередной постмортем микро-SaaS'а в Телеге.</p><p>Без маркетинга и успешного успеха. Только боль, костыли и суровый прод! Мой пет-проект - бот «Щёлк-ГДЗ». Это ИИ-помощник, который решает школьные задачки по фоткам, видео, гс, тг-кружкам и PDF. Под капотом <b>Python 3.12.3</b>, <b>aiogram 3</b>, <b>aiosqlite</b>,<b> апи OpenRouter </b>(модель Gemini 3.0 Flash) и <b>Робокасса</b>. Крутится всё это на дешёвом VPS в Нидерландах. Держит 2500+ пользователей и не падает.</p><p>В этой статье расскажу, как за 9 месяцев построил логичную архитектуру, победил ошибку FloodWait при стриминге ответа ИИ и почему в условиях 1 гига RAM обычная SQLite - отличное решение.</p><h2>Нейронка вместо джуна и Уроборос багов</h2><p>Сразу признаюсь... С нуля я это не писал :) Синтаксис мне генерили LLM-ки. Начинал с Gemini 2.5 Pro, затем перешел на 3.0 Pro, а сейчас использую 3.1 Pro.</p><p>Многие думают, что нейронка сама напишет проект "под ключ", но это миф. Я <b>никогда </b>не доверял ИИ проектирование архитектуры и использовал его как продвинутый<i> StackOverflow</i> (скармливал конкретную задачу (например, написать SQL-миграцию) и получал кусок кода).</p><p><i>ИИ — это не архитектор, а джун на спидах. </i></p><p>Как только логика усложнялась - гемини ловил <b>«Уроборос багов»</b>. Кидаешь баг <b>А</b> - он его фиксит, но появляется ошибка <b>Б</b>. Скармливаешь и её - фиксит, но возвращается баг <b>А</b>. Цикл замкнулся. Лечилось только созданием новых чатов, в которые я кидал код и писал запросы типа "Найди критические ошибки, логические дыры и баги".</p><p>Про продуктовую логику нейронка вообще не слышала. В первой версии рефералки был баг, где любой(если его аккаунта ещё нет в БД) мог написать в конце ссылки что любые 9 цифр (<i>?start=1234567890</i>) и получить бонусы.</p><p>Также были ошибки с гонкой состояний при оплатах и активациях промокодов, которые тоже фиксил запросами в новые чаты с ИИ. Так я исправил около 20 архитектурных дыр.</p><h2>Немного про архитектуру.</h2><p>Чтобы код не превратился в нечитаемую лапшу на 5000 строк, я жестко разбил всё на модули. Архитектура бота выглядит так:</p><figure><img src="https://media.tproger.ru/user-uploads/139416/2026-07-08/dfc8dd70-365f-4f70-bbbf-2b00b503bef4.webp" alt="" /></figure><p><b>main.py</b> - это точка входа с настройкой логгеров, коннетами aiohttp и запуском APScheduler</p><p><b>database.py</b> - вся работа с БД</p><p><b>neiro.py</b> - логика общения с апи OpenRouter (стриминг, сжатие фоток через Pillow, JSON-контекст)</p><p><b>states.py</b> - классы состояний aiogram.fsm.state (например, PaymentProcess, GdzMode)</p><p><b>utils.py</b> - легковесные утилиты. Например лок пользователей (защита от спама запросами):</p><p>Хэндлеры вынесены в отдельную папку handlers/:</p><p><b>handlers/common_handlers.py</b> - главное меню с обработкой /start (рефералки, utm-метки).</p><p><b>handlers/gdz_handlers.py</b> - сам процесс ИИ-решения (вход в GdzMode, прием фото, видео, кружочков).</p><p><b>handlers/pay_handlers.py</b> - это логика платежей (Robokassa) и "Умная корзина".</p><p><b>handlers/tasks_handlers.py</b> - квесты (выдача премиума за подписку на каналы спонсоров).</p><p>Сборку интерфейсов вынес в <b>all_def.py</b>. Не люблю, когда в хэндлерах генерится полотно текста с кнопками. Там же лежат функции склонения слов (1 запрос, 2 запроса, 5 запросов). А в <b>settings.py </b>лежат списки с рандомными ответами бота, чтоб казался живым.</p><p>Так же в <b>settings.py</b> я сделал кэширование картинок, тоесть при первом запуске бот грузит фото меню как <b>BufferedInputFile</b>, сохраняет <b>file_id</b> от Телеграма и дальше шлет картинки моментально по ID. Сервак говорит спасибо за сэкономленный трафик)</p><p>В <b>handlers/common_handlers.py</b> находится первичная маршрутизация. Вот так обрабатываются рефералки, переходы с сайта, с рекламы и другое при старте:</p><h2>Диета по токенам</h2><p>Хранить бесконечную историю диалогов дорого и бессмысленно. В бесплатной версии храню <b>10 последних сообщений</b> (5 пар вопрос-ответ), а в преме — <b>30. </b></p><p>Но фотки весят большое кол-во токенов. Если премиум-юзер закинет 30 фоток, OpenRouter выставит мне огромный счет. В итоге я прикрутил ограничение: Из <b>30 сообщений</b> ИИ видит только <b>10 последних картинок. </b></p><p>Старые фотки тупо вырезаю из JSON. Подменяю на системный промпт:</p><p>С довольно неплохой моделью (gemini 3.0 flash) это работает как часы (она честно признается, что забыла картинку, а не выдумывает что-то из воздуха).</p><h2>Стриминг, FloodWait и защита баланса</h2><p>Чтобы бот не выглядел тормозом, я сделал стриминг ответа от ИИ в <b>neiro.py</b>. Я обновляю сообщение в Телеграме чанками по мере получения их от ОпенРоутера.</p><p>Но если делать <b>message.edit_text </b>слишком часто, ловишь <b>FloodWait</b>. В итоге я выставил интервал в 0.7 секунд и обернул всё в жесткий<b> try/except</b>:</p><p>Стрим при этом не прерывается. Поспали и погнали дальше :) Если на этапе обработки файла или стриминга падает критическая ошибка - честно возвращаю юзеру запрос на баланс.</p><p>В <b>handlers/gdz_handlers.py</b> это выглядит так:</p><h2>SQLite тащит</h2><p>Почему не <b>Postgre</b>? Потому что для микро-SaaS с 2,5к пользователей<b> SQLite</b> хватает за глаза. Но в асинхронной среде она любит кидать ошибку <b>database is locked</b>.</p><p>Чтобы этого избежать, я включил <b>WAL-режим</b> при инициализации пула, разделил коннекты на <b>db_writer</b> и <b>db_reader</b>, а сложные операции доверил самому <b>SQL</b>.</p><p>Например, 00:00 запускается крон-таска, которая собирает огромную аналитику, начисляет всем активным юзерам +1 ежедневный запрос и сбрасывает просроченные подписки. И это всё это работает атомарно внутри <b>database.py</b>:</p><h2>Умная корзина на APScheduler</h2><p>Когда дело дошло до монетизации, всплыли две проблемы:</p><p>Первая: юзер оплатил, но забыл нажать кнопку <b>«✅ Я оплатил»</b> в боте. Бот ждет, юзер ждет, товар не выдается, поддержка кипит.</p><p>Вторая: юзер сформировал счёт и передумал ("брошенная корзина").</p><p>Вместе с LLM я с нуля изучил <b>apscheduler </b>и убил двух зайцев фоновыми задачами. Теперь в <b>handlers/pay_handlers.py </b>при генерации ссылки на оплату я создаю две отложенные таски:</p><h4>Как это работает?</h4><p>Через 5 минут срабатывает <b>async def auto_check_payment()</b>, которая тихо стучится в робокассу. Если статус<b> success</b> - бот сам начисляет запросы на баланс и радует клиента. Если статус <b>pending</b> - таска умирает, и в дело вступает 30-минутная таска.</p><p>Но она не шлёт спам вслепую, а лезит в БД и проверяет 3 бизнес-правила:</p><p>1. <b>await db.has_successful_payment_recently</b> - Не купил ли он другой товар за последние 3 часа?</p><p>2. <b>await db.get_latest_invoice_id</b> - А это точно самый последний сгенерированный им счет?</p><p>3. <b>await db.can_send_agitation</b> - Не присылали ли мы ему агитацию недавно?</p><p>Если проверки пройдены, то юзер получает сообщение: <i>"⏳ Домашка сама себя не решит! Ты начал оформлять покупку, но оплата так и не прошла..."</i>. Это поднимает конверсию оплат.</p><h2>Финал</h2><p>Почему я не использую <b>Redis </b>для стейтов? Ответ банален: мой дешевый VPS имеет всего 1 гб оперативки. Пул <b>aiohttp</b>, In-Memory стейты и асинхронные таски и так жрут 70% RAM. Редис тупо не влезет.</p><p>Как говорится, <i>работает — не трогай.</i></p><p>Сейчас проект обзавелся сайтом-витриной и продолжает развиваться. В планах - искать и чинить новые баги. Перееду на более мощное железо, когда сервак начнет физически задыхаться.</p><p>Готов ответить на вопросы по архитектуре и послушать советы в комментариях \(^^)/</p><p><i>P.s. вот ссылка на первую статью о моём проекте: </i></p><p>P.s. Если кто-то хочет потестить вживую(не реклама), то юз бота в тг <a>@gdzshchelk_bot</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Git в Telegram: как я избавился от JSON, победил Markdown и получил security by design</title>
      <link>https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu</link>
      <comments>https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Robin Gad]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu</guid>
      <description><![CDATA[<p>Git в Telegram? Без JSON, с SQLite, победой над Markdown и security by design. Код, схема БД, факапы и ссылка на бота.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/git-v-telegram--kak-ya-izbavilsya-ot-json--pobedil-markdown-i-polu">Git в Telegram: как я избавился от JSON, победил Markdown и получил security by design</a>»</p>]]></description>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[GitLab]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[IBM]]></category>
      <category><![CDATA[Мессенджер]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Markdown]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Jul 2026 06:39:38 GMT</pubDate>
      <content:encoded><![CDATA[<p>В своём Telegram-канале я время от времени предлагаю подписчикам выбрать очередную «бредовую» идею для реализации. На этот раз победил Git в Telegram: чтобы можно было инитить проекты, пушить файлы, коммитить — и всё это прямо в мессенджере.</p><p>С практической точки зрения проект на**й не нужен. Есть GitHub, GitLab и куча нормальных инструментов. Но как эксперимент — почему бы и нет? Чисто посмотреть, можно ли заставить Telegram работать как VCS.</p><h2>Почему не JSON</h2><p>На старте я думал: «Положу всё в JSON, на кой мне база данных?» Проектов мало, пользователей немного, файлы текстовые — чего заморачиваться?</p><p>Подергал JSON туда-сюда пару дней и понял: не варик.</p><ol><li>Конкурентный доступ. Два юзера одновременно коммитят — один перезаписывает файл другого.</li><li>Целостность данных. Если бот упал в середине записи — JSON остаётся в невалидном состоянии.</li><li>Версионность. Хранить историю изменений в JSON — это просто перенести проблему из кода в структуру файла.</li></ol><p>Вывод: JSON — для конфигов, а не для данных, которые меняются каждую секунду.</p><h2>Выбор SQLite и схема БД</h2><p>Выбрал SQLite, потому что:</p><ul><li>Не надо поднимать отдельный сервер</li><li>Целостность данных на уровне движка (транзакции, foreign keys, rollback)</li><li>Всё в одном файле — скопировал и унёс</li></ul><p>Сущности:</p><ul><li>users — telegram_id, username, current_project_id</li><li>projects — owner_id, name, thread_id (каждый проект живёт в своём треде канала)</li><li>files — filename, current_version, флаг modified (файл изменился и готов к коммиту)</li><li>file_versions — мясо. Каждая версия файла с полным содержимым. Привязана к file_id и опционально к commit_id</li><li>commits — сообщение, время, ссылка на проект</li><li>commit_files — связка коммитов с версиями файлов (many-to-many, чтобы поддержать ветки)</li></ul><p>Почему так: я хотел иметь возможность откатиться к любой версии любого файла. Да, база распухнет, но текстовые файлы — это не гигабайты видео. Плюс наличие file_versions и commit_files позволяет делать diff между версиями и смотреть историю изменений.</p><p>В коде вместо голых кортежей из SQL — датаклассы:</p><h2>Маркдауновый ад</h2><p>Казалось бы: взял код, обернул в тройные апострофы, кинул в Telegram. Telegram сам подсветит синтаксис, если указать язык. Красота. В теории.</p><p>На практике Telegram использует свой диалект Markdown, где куча служебных символов: _ * [ ] ( ) ~ &gt; # + - = | { } . !</p><p>Попытка 1: заэкранировать всё подряд. Результат: код превращается в кашу. Вместо<b> </b><i>def  __init__- </i> получается <i>def |_|_init|_|_ </i> — уже не запустишь, и в канале выглядит как говно.</p><p>Попытка 2: не экранировать вообще. Telegram шлёт на**й с ошибкой «can't parse entities».</p><p>Попытка 3: экранировать только то, что реально ломает разметку. Выяснилось, что порядок важен. Сначала экранируем точки и подчеркивания, потом обратную косую черту. Но и это не панацея — последовательности типа \*</p><p>после экранирования превращаются в \\*, и Telegram снова недоволен.</p><p>Попытка 4: разбивать на части. Для больших файлов делаю превью (первые 50 строк), экранирую их, отправляю как код, а полную версию — файлом. И тут начался ад: Telegram находил ошибки в тех частях кода, которых в превью вообще не было. Оказывается, он всё равно парсил полный код, даже если отправлялась только его часть.</p><p>Попытка 5 (финал): забил на Markdown и перешёл на HTML. Telegram умеет его принимать. Да, он не такой красивый, но зато предсказуемый:</p><p>Никаких точек, подчеркиваний, обратных слешей. Просто экранируем три символа — и код летит как надо.</p><h2>Security by design</h2><p>Когда бот начал обрастать функциями, я задумался о безопасности. Чтобы никто не мог коммитить или удалять чужие файлы, начал писать проверки в каждую команду:</p><p>Добавил в /commit. Потом решил с другого аккаунта потестировать команды на чужих файлах. И тут бот на каждую команду стал выдавать «файл не найден» или «проект не найден». И я понял: безопасность уже работает. С самого начала. Из коробки. Без единой строчки кода.</p><p>Как так вышло? В таблице projects с самого начала было поле owner_id. При создании проекта я писал туда telegram_id  владельца. Все запросы к БД фильтруются по этому полю:</p><p>Показать проекты — только свои. Найти файл — только в своих проектах. Выбрать проект — только из своих. Никаких лишних проверок. Просто SQL-запросы, которые с самого начала учитывали владельца.</p><h2>Команды: от семи до двух десятков</h2><p>Изначально казалось, что команд будет немного. Но...</p><p>Жизнь рассудила иначе.</p><ul><li>База: /start, /init, /use, /list, /ls, /commit, /log, /status</li><li>Удаление: /rm, /rmproject + подтверждение</li><li>Игнор: /ignore, /ignored, /unignore</li><li>Ветки: /branch, /branches, /checkout</li><li>Диффы и просмотр: /diff, /cat</li></ul><p>Итого — уже под два десятка. И это не предел.</p><h2>Простота &gt; абстракции</h2><p>В моём коде нет абстрактных базовых классов. Совсем. Потому что они нужны только когда у тебя есть минимум две разные реализации одного и того же. В GitGram всё проще: один способ работать с БД, один способ шифровать, один способ парсить .gitignore.</p><p>Если завтра появится вторая реализация — тогда и буду делать интерфейс. А пока это просто оверхед.</p><p>В GitGram:</p><ul><li>Хочешь понять, как работает add_file — идёшь в database.py и читаешь 10 строк кода.</li><li>Хочешь увидеть обработчик /commit — открываешь bot.py и смотришь.</li></ul><p>Никаких AbstractMinerShieldEventProcessor, BaseGitGramManager, InterfaceProviderFactory.</p><p>Код должен быть тупым. Чем тупее — тем проще его читать и отлаживать.</p><h2>Что дальше</h2><p>В планах:</p><ul><li>Докрутить коллаборацию (несколько человек над одним проектом)</li><li>Приватные репозитории</li><li>Кодспейс прямо в боте (да да знаю я сошёл с ума и бла бла бла..)</li></ul><h2>Итог</h2><p>GitGram принимает файлы, режет их на куски если надо, постит в канал с подсветкой. Коммиты ходят, ветки переключаются, диффы показываются. Всё это в тредах, каждый проект отдельно.</p><p>На практике GitGram ни к чему. Но сама задумка — Git в Telegram — это же так прикольно. Просто посмотреть, можно ли такое вообще запилить.</p><h2>Если хочешь поучаствовать</h2><ul><li><a href="https://t.me/Git_Gram/314" rel="nofollow">GitGram</a></li><li>Чат для хардкорных: <a href="http://t.me/sandbox_hardcore" rel="nofollow">@sandbox_hardcore</a> — вся сырая разработка, факапы и обсуждения (без цензуры)</li></ul><p>Подписывайся на мой <a href="https://t.me/+q0NBWy428y1hODEy" rel="nofollow">TГ-канал</a> — там я публикую все свои эксперименты, код и приглашаю к обсуждению. Только факты, мат и никакой политоты.</p>]]></content:encoded>
    </item>
    <item>
      <title>Локализация через Enum, неожиданный Дзен, быстрее только телепатия</title>
      <link>https://tproger.ru/articles/lokalizaciya-cherez-enum-neozhidannyj-dzen-bystree-tolko-telepat</link>
      <comments>https://tproger.ru/articles/lokalizaciya-cherez-enum-neozhidannyj-dzen-bystree-tolko-telepat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Самир Гёзалов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/lokalizaciya-cherez-enum-neozhidannyj-dzen-bystree-tolko-telepat</guid>
      <description><![CDATA[<p>Надоело плодить JSON/ARB файлы при локализации Flutter-приложения? Автор делился личным опытом и показал, как элегантно настроить локализацию через Enum без внешних зависимостей, генераторов кода и боли в рантайме.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/lokalizaciya-cherez-enum-neozhidannyj-dzen-bystree-tolko-telepat">Локализация через Enum, неожиданный Дзен, быстрее только телепатия</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Алгоритмы и структуры данных]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Массивы и строки]]></category>
      <category><![CDATA[Красивый хак]]></category>
      <category><![CDATA[Android]]></category>
      <category><![CDATA[Мобильная разработка]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Flutter]]></category>
      <category><![CDATA[Dart]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 04 Jun 2026 04:45:17 GMT</pubDate>
      <content:encoded><![CDATA[<p>Решил давеча добавить локализацию в свое приложение на Flutter. Задачка-то простая: пара кнопок, пару десятков переменных. Казалось бы, делов на пять минут. Но Flutter «из коробки» сразу попытался всучить мне какую-то дичь в виде ARB и JSON файлов. Хмм, из прошлого с такими реализациями лишь печаль, так что…</p><h2>Попытка №1. Путь в лоб: Классы и интерфейсы</h2><p>Самый очевидный способ - создать родительский класс с переменными, а языки сделать его наследниками. Но это просто… фиаско. Даже если не писать from/toJson, процесс выглядит так:</p><ol><li>Объявил переменную в родителе.</li><li>Прописал её в конструкторе.</li><li>Повторил то же самое для всех дочерних классов (всех языков).</li></ol><p>Если бы я работал на аутсорсе в Индии и мне платили за количество строк кода - это был бы идеальный вариант. Но я хотел, чтобы одной строки при объявлении было достаточно.</p><h2>Попытка №2. Стандарт (ARB/JSON)</h2><p>Я поплевался, но решил попробовать - «стандарт» всё-таки. Мало ли, может чего поменялось за годы. Вроде всё завелось, но сам процесс… это боль. Бегать по разным файлам, чтобы добавить одну строчку - так себе удовольствие.</p><p>Почему я от него окончательно отказался? Когда данных становится реально много (десятки языков, тысячи строк), ты попадаешь в ловушку: тебе нужно эту махину либо целиком держать в памяти, либо постоянно подгружать и парсить. Ради смены одного слова на кнопке заставлять девайс ворочать тяжелые JSON-ы в рантайме - так себе затея для производительности.</p><h2>Попытка №3. Таблицы и костыли</h2><p>Подумал: «Окей, почему бы не подтянуть старый добрый CSV или вообще закинуть всё в табличку?». И тут официальный пакет локализации сказал: «Извини, мужик, тут наши полномочия всё».</p><p>Ну, я тоже не пальцем деланный. Решил припахать нейросеть, чтобы она написала мне собственный генератор. Флоу получился такой:</p><ol><li>Добавляю строку в таблицу.</li><li>Запускаю генератор.</li><li>Он лепит «родительский» файл.</li><li>После Freezed генерит toJson, fromJson…</li></ol><p>Короче, весело не было. Мало того, я понимал: если языков станет много, мне придется добавлять их все разом и единовременно, иначе в рантайме всё начнет плеваться ошибками. Плюс та же проблема с памятью: таблица - это структура, которую надо парсить и хранить.</p><h2>Красный флаг для программиста</h2><p>Но главная проблема даже не в этом. Необходимость запускать генератор после добавления каждой переменной - это для любого программиста красный флаг.</p><p>Что происходит на практике? Когда ты пишешь код и тебе нужно добавить одну несчастную строку, тебе лень (читать: нехочется выходить из потока творения) запускать весь этот цикл с генерацией. Ты просто её хардкодишь в надежде «потом скопом всё добавлю одним махом». А «потом» наступает тогда, когда уже весь проект завален хардкодом, и вычищать его - то еще удовольствие. Даже нейросетки с такими запросами помогают не с первого раза, и с сомнительной эффективностью.</p><p>Нутром чуял - флоу неправильный. В итоге я пришел к тому, что называю идеальной локализацией.</p><h2>Эволюция лени: почему Enum победил интерфейсы</h2><p>Я начал мучить нейросеть разными вариантами реализации. Для меня в первую очередь был важен флоу работы: мне было тупо лень писать больше одной строки кода, чтобы добавить переменную.</p><p>Моя философия проста: строку захардкодить - моментально. Добавление даже одной строки в другом файле требует доп. действий. НО, если действий минимум, то кодер поймет, что выигрыш во времени сейчас мизерный против больших потерь в будущем, и исправно добавит строку в правильное место. Этого не произойдет, если для добавления строки нужно «отчитаться» в десяти местах.</p><h2>Попытка №4. Рекорды (Records)</h2><p>Присматривался к рекордам. С ними удобно: не нужно писать конструкторы. Но есть подвох: как только ты добавил переменную в один язык, компилятор тут же сходит с ума и требует добавить её во все остальные прямо сейчас. Никакой гибкости и возможности оставить «на потом».</p><h2>Идеальный костыль: Enum</h2><p>В итоге я пришел к самому, казалось бы, «неправильному» способу, который оказался идеальным. Enum. Само название намекает на перечисления и цифры, но оказалось, что хранить в нем буквы и целые фразы - это лучший путь для локализации.</p><p>Знаете, мне моё решение так понравилось, что я пошел к нейросетям и проверил его со всех сторон. Все модели  остались в полном восторге. Под это дело я даже подготовил монументальную «нейро-статью» на полтора часа внимательного чтения, со всеми графиками, бенчмарками и анализом производительности.</p><p>Но потом я заглянул в правила Хабра, прислушался к голосу разума и понял: никто не хочет читать полтора часа сухой статистики. Лучше я просто расскажу вам свою историю «от первого лица», как я докатился до такой жизни. Ну, а если вы совсем уж фанаты цифр - просто скормите этот текст любой нейронке, она вам перескажет всё в лучших академических тонах с графиками на любой вкус.</p><p>А здесь мы будем говорить по делу.</p><p>Ниже я выкатываю сам код. Код тут не для зубрежки, а лишь чтобы указать направление / идею. Самая большая прелесть этой системы в том, что она оставляет гигантское пространство для любого типа реализации. Это архитектурный каркас, который чертовски сложно сломать. Главное — уловить принцип.</p><h2>Реализация</h2><p>Принцип разделения:</p><ul><li>enum Strings → типизированный контракт (что существует).</li><li>translator → источник данных (откуда берётся текст).</li></ul><p>Структура файлов - минимальная. Никаких внешних зависимостей:</p><h2>Ядро - strings.dart</h2><p>Весь контракт локализации в одном enum. Два режима - хардкод switch и серверный кеш - за одним и тем же</p><p>. Даже сообщения об ошибках локализации — сами локализованы:</p><h2>Список языков - languages_enum.dart</h2><p>Каждый язык - самодостаточная единица: знает свой код, название, направление текста и как себя загрузить:</p><p>Хотите грузить с сервера? Одна строка + loader. И</p><p>- тоже одна строка, потому что</p><p>уже является registry:</p><h2>Переводчик - i18n/russian.dart</h2><p>Как это выглядит в жизни (и почему я перестал хардкодить) Я намеренно не привожу здесь код оберток виджетов или конкретных стейт-менеджеров. Всё это - чистая «вкусовщина». Для работы системы нужен лишь элементарный вещатель событий (Notifier), повешенный на метод смены языка.</p><p>Но главное - это мой ежедневный флоу. Сейчас, чтобы добавить строку в UI, я просто вызываю нужный мне ключ: Strings.someKey(). Без контекста, везде.</p><p>А если ключа еще нет? Я тупо иду в Strings и добавляю одну строчку в Enum. Всё.</p><p>Благодаря дефолтному конструктору, приложению абсолютно плевать, что у меня там еще 100 языков не переведены. Оно компилируется и работает здесь и сейчас. А дальше в дело вступают гит-хуки: при комите нейросетка сама подхватывает изменения и заполняет недостающие поля в переводчиках.</p><p>Знаете, какое самое странное чувство? Мне сейчас реально проще и быстрее завести переменную в локализации, чем хардкодить строку в коде. Кажется, это и есть признак здоровой архитектуры - когда делать «правильно» становится физически удобнее, чем делать «быстро» и криво.</p><p>Собственно если в кратце это все о чем я хотел рассказать. Если тема зайдет или кто-то сам не сможет допереть, как прикрутить это к своей архитектуре - пишите в комментариях, разберемся.</p><p>Надеюсь, мой опыт сэкономит вам пару литров нервных клеток. Всех благ и чистого кода без «магических строк»!</p>]]></content:encoded>
    </item>
    <item>
      <title>Адаптация открытого симулятора InferSim для оценки загрузки промышленных GPU</title>
      <link>https://tproger.ru/articles/adaptaciya-otkrytogo-simulyatora-infersim-dlya-ocenki-zagruzki-prom</link>
      <comments>https://tproger.ru/articles/adaptaciya-otkrytogo-simulyatora-infersim-dlya-ocenki-zagruzki-prom?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дмитрий Ходыкин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/adaptaciya-otkrytogo-simulyatora-infersim-dlya-ocenki-zagruzki-prom</guid>
      <description><![CDATA[<p>Рассказываем, как доработали симулятор InferSim от Alibaba: добавили поддержку новых GPU (включая MetaX C500), расширили список моделей с гибридными архитектурами и сделали визуализацию на Streamlit. Инструмент позволяет оценивать задержки и требуемую память без запуска реального инференса и помогает избежать грубых ошибок при планировании закупок оборудования.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/adaptaciya-otkrytogo-simulyatora-infersim-dlya-ocenki-zagruzki-prom">Адаптация открытого симулятора InferSim для оценки загрузки промышленных GPU</a>»</p>]]></description>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[Визуализация]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 04 May 2026 08:09:55 GMT</pubDate>
      <content:encoded><![CDATA[<p>Планирование аппаратных ресурсов под обслуживание больших языковых моделей — задача с высоким порогом ошибки. Потратишь лишнего — получишь неоправданные расходы. Сэкономишь — столкнёшься с деградацией пользовательского опыта. На российском рынке, где доступ к современным ускорителям ограничен, эта задача становится особенно острой.</p><p>Мы остановились на открытом симуляторе InferSim от Alibaba. Он умеет считать TTFT, TPOT и пропускную способность без запуска реального инференса. Но из коробки поддерживает только несколько топовых GPU и фиксированный набор моделей — для наших сценариев этого было недостаточно. Пришлось дорабатывать.</p><p><b>Как устроен InferSim</b></p><p>В основе симулятора — двухфазная схема. Сначала на целевом железе запускаются микро-бенчмарки: измеряется реальная производительность на типовых операциях внимания и матричных умножениях. Получается таблица коэффициентов Model FLOPs Utilization (MFU) — сколько процентов от теоретического максимума выдаёт конкретная карта на конкретной операции. Затем, уже без доступа к GPU, симулятор на основе этих MFU и аналитической модели вычисляет задержки. Именно такой подход даёт более точные предсказания, чем оценка по паспортной пропускной способности памяти.</p><p><b>Добавляем своё железо</b></p><p>Первым делом мы внесли в hardware/gpu.py поддержку MetaX C500 64GB. Характеристики добавляются через dataclass-экземпляры:</p><p>После этого c500 попадает в словарь gpu_map, и симулятор запускается с ключом --device-type C500 --world-size 2. Значения FLOPS и пропускной способности пришлось оценивать по косвенным источникам — производитель не публикует точных цифр. Позже планируем уточнить их через бенчмаркинг на реальном оборудовании. Таким же способом добавили Nvidia 1xH100 и A100.</p><p><b>Конфигурация моделей и одна болезненная ошибка</b></p><p>Симулятор ожидает стандартный config.json в формате Hugging Face. Для Qwen3-32B подготовили файл qwen3_32b_config.json:</p><p>Ключевой момент — правильное значение head_dim. У Qwen3-32B оно равно hidden_size / num_attention_heads = 5120 / 64 = 80. У нас ушло некоторое время, чтобы понять, почему симулятор выдаёт невалидные результаты: изначально в конфиге стояло значение 128. Пока не исправили — KV-кеш переоценивался, и метрики улетали в неадекватные цифры. После исправления всё встало на свои места.</p><p>Позже добавили поддержку Qwen3.5‑9B с её гибридной архитектурой, построенной на чередовании Gated DeltaNet и Gated Attention. Главная особенность модели в том, что около трёх четвертей слоёв не создают привычного KV‑кеша, а используют линейное внимание – компактное скрытое состояние, которое лишь обновляется с каждым новым токеном, не увеличиваясь в объёме. Это кардинально снижает расход памяти на длинных контекстах, но привносит и свою цену: на коротких дистанциях такая модель проигрывает в скорости Prefill, потому что Gated DeltaNet работает последовательно и хуже утилизирует матричные вычисления GPU.</p><p>Симулятор изначально не умел различать слои двух типов и считал весь KV‑кеш одинаково. Чтобы поддержать Qwen3.5, пришлось доработать расчёт задержек в классе HybridModel: теперь он отдельно обсчитывает full‑attention‑слои с полным кешем и linear‑attention‑слои без кеша, используя параметры num_full_attn_layers и num_linear_attn_layers из конфигурации. В директорию проекта models добавили hybrid_model.py, в котором сделали дополнительные расчёты:</p><p>Без такой дифференциации симулятор для Qwen3.5‑9B показывал E2E порядка 2 500 секунд вместо реальных нескольких секунд – та ошибка, которую мы долго отлавливали.</p><p><b>Визуализация и эксплуатация</b></p><figure><img src="https://media.tproger.ru/user-uploads/137657/2026-04-30/d68ad2ca-cbc0-4fe3-a4af-2f082a55ecdf.webp" alt="" /><figcaption>Пользовательский интерфейс на Streamlit</figcaption></figure><p>Чтобы не разбирать каждый раз текстовый вывод InferSim, сделали веб-интерфейс на Streamlit. Симулятор дёргается через subprocess, результаты парсятся из stdout и визуализируются:</p><ul><li>тепловые карты задержек (Prefill, Decode, E2E Total) для разных длин входных и выходных токенов;</li><li>анализ RPS с расчётом требуемой параллельности и сравнением с доступной памятью;</li><li>информация о занятой памяти в формате «X ГБ из Y ГБ».</li></ul><p>Кэширование через @st.cache_data позволило избежать повторных запусков симуляции при неизменных параметрах. Интерфейс получился достаточно удобным, чтобы даже коллеги без погружения в командную строку могли осмысленно сравнивать конфигурации.</p><p>Приложение упаковано в Docker и лежит в репозитории на <a href="https://github.com/DmitriyKhodykin/InferSim" rel="nofollow">GitHub</a>. Сервисы — сам Streamlit и Nginx в качестве обратного прокси с SSL-терминацией и базовой HTTP-аутентификацией. Деплой на VDS автоматизирован через GitHub Actions: собрали образ, отправили на сервер, перезапустили контейнеры. SSL-сертификаты Let's Encrypt обновляются по cron.</p><p><b>Что в итоге</b></p><p>Адаптированный InferSim не претендует на абсолютную точность — она ограничена качеством бенчмарков и коэффициентов MFU. Но инструмент позволяет избежать грубых ошибок при планировании, что в условиях ограниченного доступа к GPU и их высокой стоимости само по себе немало. Мы продолжаем калибровку симулятора и готовим обновлённые конфигурации для следующих моделей.</p><p>Репозиторий проекта открыт, будем рады замечаниям и предложениям от тех, кто решает схожие задачи.</p>]]></content:encoded>
    </item>
    <item>
      <title>10 подходов по работе с данными, которые должен знать каждый data-инженер</title>
      <link>https://tproger.ru/articles/10-podhodov-po-rabote-s-dannymi--kotorye-dolzhen-znat-kazhdyj-dat</link>
      <comments>https://tproger.ru/articles/10-podhodov-po-rabote-s-dannymi--kotorye-dolzhen-znat-kazhdyj-dat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Неопознанный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/10-podhodov-po-rabote-s-dannymi--kotorye-dolzhen-znat-kazhdyj-dat</guid>
      <description><![CDATA[<p>10 подходов по работе с данными для data-инженера</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/10-podhodov-po-rabote-s-dannymi--kotorye-dolzhen-znat-kazhdyj-dat">10 подходов по работе с данными, которые должен знать каждый data-инженер</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 24 Mar 2026 08:55:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В статье рассматриваем 10 подходов по работе с данными, которые должен знать любой уважающий себя data-инженер.</p><h2>10. Star Schema —
старая рабочая лошадка (которая разваливается на больших данных)</h2><p>Наша любимая звездочка — это самая базовая модель для аналитики. Таблица фактов
(с метриками, напр. продажи) окружена таблицами измерений.</p><p>К примеру, таблица
фактов «Продажи», а вокруг нее таблицы измерения: «Клиент» (кому продали), «Продукт»
(что продали), «Заказ» и т.д. Но на больших объемах, а также при увеличении
объектов в хранилище данных, она может начать подводить. Особенно сложно в нее
вносить какие-либо изменения, так как таблицы зависят друг от друга. Звездочка
это такой неэластичный монолит.</p><p><b>Проблемы на практике:</b></p><p>Когда таблица fact_sales растёт до сотен миллионов или
     миллиардов строк, запросы начинают жёстко тормозить. JOIN-ы с несколькими измерениями и GROUP BY приводят к долгим
     сканированиям. сложно вносить изменения, все может посыпаться</p><p>Типичный запрос может выглядеть так:</p><p>И чем больше строк в fact_sales, тем медленнее выполняется такой
запрос.</p><p>Проще говоря: звёздочка удобна и понятна, но <b>не очень масштабируется</b>.</p><h2>9. Snowflake Schema — слишком усложнённая и медлительная</h2><p>Снежинка — это “нормализованная” версия звездочки: таблицы измерений
разбиваются на ещё более мелкие части, как ветви снежинки. В результате каждая
иерархия измерений (например продукт → категория → бренд) живёт в своей
таблице.</p><p>Это улучшает хранение (меньше дублирования данных и меньше места на диске),
но делает запросы <b>еще более тяжёлыми</b>, потому что для простого отчёта
нужно больше JOIN-ов между таблицами. Зато теперь чуточку легче поддерживать и
это уже мене похоже на монолит, хотя все равно таковым является.</p><p><b>Короче говоря:</b></p><ul><li>Плюсы: меньше избыточности, данные лучше
     структурированы, нормализовано (хорошо для поддержки целостности).</li><li>Минусы: запросы становятся сложнее и медленнее (много
     JOIN-ов), сложнее для понимания тем, кто пишет SQL-аналитику. И все равно
     монолит, который сложно поддерживать.</li></ul><p>п=</p><h2>8. Data Vault — гибко,
масштабируемо… и очень сложно</h2><p>Эта модель появилась, чтобы строить очень
масштабируемые DWH, где можно спокойно переживать постоянные изменения
источников данных.</p><p>Вместо привычных «фактов и измерений» тут три типа таблиц:</p><p>·        
<b>Hubs
</b>— бизнес-сущности (клиент, заказ, продукт).</p><p>·        
<b>Links
</b>— связи между ними.</p><p>·        
<b>Satellites
</b>— атрибуты и история изменений (фио клиента, наименование товара, стоимость
товара).</p><p>Можно добавлять новые источники почти без боли и хранить полную историю изменений.</p><p>Но где
подвох?</p><p>Во-первых, схема получается гигантской — десятки и сотни таблиц.</p><p>И во-вторых, даже простой отчёт превращается в цепочку JOIN-ов на пол-экрана SQL.</p><p>А в-третьих, новым людям в команде разобраться в этом всём,
как отдельный квест.</p><p>Data Vault отлично подходит для
enterprise-DWH, где важны аудит, история и масштаб. Но для обычной аналитики это как резать колбасу бензопилой.</p><p>Пример запроса:</p><h2>7. Wide Tables / One Big Table (OBT) — для time-series</h2><p>Это противоположность сложным моделям вроде Data Vault.</p><p>Идея максимально простая - берём данные из
разных таблиц и склеиваем всё в одну огромную таблицу.</p><p>·        
Запросы супербыстрые, почти без
JOIN-ов.</p><p>·        
Очень удобно для BI и дашбордов.</p><p>·        
Понятная структура: «одна строка = один
бизнес-объект».</p><p>Но за скорость нужно платить следующими недостатками:</p><p>·        
Дублирование данных на каждом шаге,</p><p>·        
Таблица быстро разрастается до сотен колонок,</p><p>·        
Любое изменение логики требует пересборки всей
таблицы,</p><p>·        
Легко поймать несогласованность данных.</p><p>Короче:</p><p>OBT — это как кеш. Но все это добро
практически невозможно поддерживать.</p><p>Часто применяется в рамках time-seriesанализа:
IoT, анализ метрик, логов, clickstream.</p><p>Пример запроса:</p><h2>6. Graph Models (Neo4j, TigerGraph) — для связей</h2><p>Когда ценность данных именно <b>в связях</b> (например,
в мошеннических схемах, социальном влиянии или цепочках переходов по сети). В таком случае куча
join-ов просто
перестают работать, особенно при всякого рода рекурсиях.</p><p>С этим помогают графовые базы.</p><p>Вот к примеру, нужно нам найти всех
друзей наших друзей:</p><ul><li>Количество JOIN-ов растёт экспоненциально
     с каждым уровнем.</li><li>Уже на 3+ переходах запрос
     становится почти нечитаемым.</li><li>Кратно падает
     производительность</li></ul><p>Теперь посмотрим, как в графовой
базе это будет реализовано:</p><p>Отлично
подходит для антифрода, соцграфов и рекомендательных систем.</p><p>И совершенно
не подходит для OLAP-аналитики.</p><h2>5. Streaming Event
Sourcing (Kafka + CDC)</h2><p>Классический batch-ETL плохо сочетается с системами реального времени. Для real-time аналитики более
всего подходит CDC.</p><p>CDC превращает изменения в базе данных в события, которые отправляются потребителю
(DWH).</p><p>Данные становятся не снимком, а временной линией.</p><p><b>Как все это работает</b></p><p>·        
Базы данных → источники событий</p><p>·        
Kafka → надёжный журнал событий</p><p>·        
Потребители → могут пересобрать состояние в
любой момент</p><p>Пример (из debezium):</p><h2>Плюсы</h2><p>·        данные в реальном времени</p><p>·        
встроенное восстановление после сбоев</p><p>·        
слабая зависимость между продюсерами и
консьюмерами</p><h2>Минусы</h2><p>·        
высокая сложность реализации</p><p>·        
порядок событий и идемпотентность реализовать
непросто</p><p>·        
отладка требует видимости на уровне событий</p><h2>4. Columnar Storage (Parquet, Delta Lake) для дешёвой и быстрой аналитики</h2><p>Row
oriented базы
(данные стандартно лежат в виде строк в таблицах) оптимизированы под точечные
запросы, а не под аналитику. Если вы выбираете из таблицы всего два столбца из
ста, все равно будет считываться весь файл, так как все столбы хранятся в одном
файле.</p><p>Колоночный же формат подразумевает, что значения столбцов лежат в разных
местах.</p><h2>Ключевые преимущества</h2><p>·        
читаются только нужные колонки</p><p>·        
сильное сжатие (RLE, словарное кодирование)</p><p>·        
векторизованное выполнение</p><p>Меньше I/O → быстрее запросы.</p><p>Структура таблицы:</p><h2>3. Мульти-модельные (когда SQL и NoSQL одновременно)</h2><p>В реальной жизни данные редко бывают одного типа.</p><p>Современные приложения одновременно работают с:</p><p>·        
реляционными фактами</p><p>·        
полуструктурированным JSON</p><p>·        
связями между сущностями</p><p>Мульти-модельные базы позволяют запрашивать всё это в одном месте.</p><p>Вот типичный сценарий, когда один из столбцов хранит данные в jsonb:</p><p>И вот пример запроса к такой таблице:</p><p>Как видно для аналитика это не совсем удобно. Поэтому (как в
сценарии с data vault),
для аналитиков лучше делать отдельные витрины, где данный json уже разложен на нужные столбцы.</p><h2>Плюсы</h2><p>·        
одна система для разных типов данных</p><p>·        
мощный SQL при гибкой схеме</p><p>·        
меньше ETL и копирования данных</p><h2>Минусы</h2><p>·        
сложнее управлять схемой</p><p>·        
планы запросов могут усложняться</p><p>·        
медленнее специализированных движков</p><h2>2. Reverse ETL (операционная аналитика, возвращающая данные в приложения)</h2><p>Классическая аналитика заканчивается дашбордами и отчетами.</p><p>Reverse ETL замыкает цикл: данные из хранилища отправляются обратно в
операционные системы. Будь то скорректированные данные или какие-то посчитанные
в DWH метрики,
которые должны также храниться в информационной системе, что отображаться на
сайте или еще где-нибудь.</p><p>Вот частые примеры использования:</p><p>·        
почти-реальная персонализация</p><p>·        
актуальные метрики здоровья клиента и оттока</p><p>·        
автоматизация продаж и маркетинга (CRM, ESP и
т.д.)</p><h2>1. Единый слой данных (наше будущее)</h2><p>Вот обычная картина в средней компании: операционные данные
хранятся в PostgreSQL, аналитические
данные в Greenplum, стриминговые
данные в Clickhouse, данные
для поиска в Elasticsearch.
Четыре системы, между которыми данные нужно синхронизировать и реплицировать.</p><p>Unified Serving Layer использует единый логический слой таблиц</p><p>Например, Iceberg / Hudi / Delta с разными способами доступа.</p><p>То есть, один
набор данных, много движков, без ETL.</p><p>Данные лежат в одном месте, но читаются разными движками,
предназначенными для разных задач.</p><h2>Что это заменяет</h2><p>·        
нет ETL (warehouse → lake → feature store)</p><p>·        
рассинхронизацию данных между системами</p><p>·        
повторную реализацию логики в SQL, Spark и
приложениях</p><p>Пример таблицы в Iceberg</p><p>OLAP-запрос (Trino / Spark SQL)</p><h2>Плюсы</h2><p>·        
единый источник истины</p><p>·        
доступ из разных движков (SQL, ML, стриминг)</p><p>·        
ACID, time-travel, эволюция схемы</p><p>·        
отсутствие vendor lock-in</p><h2>Минусы</h2><p>·        
более сложная платформа</p><p>·        
требуется сильное управление метаданными и
governance</p><p>·        
не является прямой заменой OLTP-БД (потому что с
нагрузкой может не справиться).</p>]]></content:encoded>
    </item>
    <item>
      <title>Андрей Карпати выложил LLM Council — инструмент, где ИИ спорят и выбирают лучший ответ</title>
      <link>https://tproger.ru/news/so-osnovatel-openai-karpati-vylozhil-llm-council---instrument--gde-ii-sporyat-i-vybirayut-luchwij-otvet</link>
      <comments>https://tproger.ru/news/so-osnovatel-openai-karpati-vylozhil-llm-council---instrument--gde-ii-sporyat-i-vybirayut-luchwij-otvet?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/so-osnovatel-openai-karpati-vylozhil-llm-council---instrument--gde-ii-sporyat-i-vybirayut-luchwij-otvet</guid>
      <description><![CDATA[<p>Андрей Карпати выложил LLM Council — инструмент, где несколько ИИ спорят, оценивают ответы друг друга и формируют общий «советский» финальный вывод</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/so-osnovatel-openai-karpati-vylozhil-llm-council---instrument--gde-ii-sporyat-i-vybirayut-luchwij-otvet">Андрей Карпати выложил LLM Council — инструмент, где ИИ спорят и выбирают лучший ответ</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 24 Nov 2025 02:52:30 GMT</pubDate>
      <content:encoded><![CDATA[<p><b>Андрей Карпати</b> <a href="https://github.com/karpathy/llm-council?tab=readme-ov-file">выложил</a> в открытый доступ <b>LLM Council</b> — локальное веб-приложение, в котором <b>несколько ИИ-моделей отвечают на один вопрос, спорят между собой и выбирают финальный ответ</b>.</p><p>Проект выглядит как ChatGPT, но под капотом — работа через OpenRouter и цепочка из трех этапов: сбор ответов, взаимная оценка и «решение совета» ﻿.</p><p>Карпати честно пишет, что это <b>«на 99% вайб-кодинг»</b>, субботний хак для души. И он не планирует развивать проект. Но сообщество уже разнесло репозиторий по соцсетям — идея демократичного «совета ИИ» оказалась вирусной.</p><h2>Как работает LLM Council</h2><p>По задумке Карпати, механизм должен показать, как <b>разные модели видят одну и ту же задачу</b> — и что они думают о чужих ответах.</p><p><b>1. Первая стадия — мнения.</b> Каждая модель получает вопрос и генерирует свой ответ. В интерфейсе они показываются в отдельных вкладках, чтобы можно было сравнить.</p><p><b>2. Взаимные ревью.</b> Затем модели получают ответы своих «коллег», но с анонимизацией имен, чтобы не было фаворитизма. Каждая модель ранжирует остальные по точности и полезности ﻿.</p><p><b>3. Финальное решение.</b> Выбранный «председатель» — любая из моделей, которую укажет пользователь — собирает всю критику, мнения и свои выводы в итоговый ответ. Получается что-то вроде консенсуса.</p><figure><img src="https://media.tproger.ru/user-uploads/111041/2025-11-24/9719d6c3-fbe4-412b-b59d-60732c6acbb0.jpeg" alt="" /></figure><h2>Что внутри</h2><p>В README Карпати описывает минималистичный стек:</p><ul><li><b>Backend:</b> FastAPI + async httpx, работа через OpenRouter API.</li><li><b>Frontend:</b> React + Vite, рендеринг через react-markdown.</li><li><b>Хранилище:</b> JSON-файлы в data/conversations/.</li><li><b>Управление проектом:</b> uv для Python, npm для фронтенда .</li></ul><p>Пользователь добавляет свой OPENROUTER_API_KEY и при желании меняет «состав совета». По умолчанию туда входят <b>GPT-5.1</b>, <b>Gemini 3 Pro Preview</b>, <b>Claude Sonnet 4.5</b> и <b>Grok-4</b>.</p><h2>Зачем это вообще нужно</h2><p>Карпати пишет, что пилил проект для совместного с LLM чтения книг и сравнения, как разные модели анализируют один и тот же материал. Но по факту инструмент получился куда интереснее:</p><ul><li>это способ быстро оценить качество моделей;</li><li>это визуализатор «множественного ИИ-мышления»;</li><li>это мини-демонстрация того, как устроены будущие агентные системы, где несколько моделей принимают решения совместно.</li></ul><p>И да, все это — буквально субботний хак, который уже <a href="https://github.com/karpathy/llm-council?tab=readme-ov-file">набрал</a> несколько тысяч звезд на GitHub.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как подключить VSCode к GitLab, Docker, Jupyter</title>
      <link>https://tproger.ru/articles/kak-podklyuchit-vscode-k-gitlab--docker--jupyter</link>
      <comments>https://tproger.ru/articles/kak-podklyuchit-vscode-k-gitlab--docker--jupyter?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-podklyuchit-vscode-k-gitlab--docker--jupyter</guid>
      <description><![CDATA[<p>Пошаговая инструкция по интеграции VSCode с GitLab, Docker и Jupyter. Как получить токен доступа, настроить Dev Container, выбрать ядро для ноутбука и объединить все инструменты разработки в одном редакторе.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-podklyuchit-vscode-k-gitlab--docker--jupyter">Как подключить VSCode к GitLab, Docker, Jupyter</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[GitLab]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 12 Nov 2025 10:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчики каждый день открывают VSCode и начинают писать код. Но редактор сам по себе — это просто текстовый блокнот с подсветкой синтаксиса.</p><p>Продуктивность разработчика повышается, когда к VSCode подключены инструменты для командной работы, контейнеризации и анализа данных. В статье по шагам разобрали, как интегрировать VSCode с GitLab, Docker и Jupyter.</p><h2>Зачем всё это подключать</h2><p>VSCode из коробки умеет работать с <b>Git</b>. Но когда ваш репозиторий лежит на GitLab, хочется создавать merge request прямо из редактора, просматривать pipeline, работать с issues.</p><p><b>Docker </b>решает проблему «у меня работает, а у тебя нет». Вы пишете код в контейнере с одними версиями библиотек, и любой член команды запустит точно такое же окружение.</p><p><b>Jupyter </b>традиционно запускают в браузере. Но переключаться между браузером и редактором неудобно. VSCode умеет работать напрямую — редактируете код, запускаете ячейки, смотрите графики в одном окне.</p><h2>Как подключить VSCode к GitLab</h2><h2>Шаг 1. Устанавливаем расширение</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-11-05/0971c3c4-ccd5-4c1a-8642-2895bb77ba7d.jpg" alt="" /></figure><p>Откройте VSCode и нажмите Ctrl+Shift+X. В поиске введите «GitLab Workflow». Установите официальное расширение. После установки в левой панели появится иконка GitLab.</p><h2>Шаг 2. Получаем токен доступа</h2><p>Расширению нужен способ общаться с вашим GitLab-сервером. Для этого создадим персональный токен:</p><ol><li>Зайдите в GitLab через браузер.</li><li>Кликните на аватар &gt;&gt; Settings &gt;&gt; Access Tokens.</li><li>Придумайте название токена.</li><li>Выберите срок действия.</li><li>Отметьте галочками права доступа: api, read_user, read_repository, write_repository.</li><li>Нажмите «Create personal access token».</li><li>Скопируйте токен — он покажется только один раз.</li></ol><h2>Шаг 3. Настраиваем расширение</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-11-05/b05bc74a-9bf9-4150-b228-b8065918ca18.jpg" alt="" /></figure><p>Вернитесь в VSCode. Нажмите Ctrl+Shift+P и введите «GitLab: Add Account». Расширение попросит два параметра:</p><ul><li><b>GitLab instance URL</b> — адрес вашего GitLab (например, https://gitlab.com или адрес корпоративного сервера).</li><li><b>Personal Access Token</b> — токен, который только что создали.</li></ul><p>Вставьте токен и нажмите Enter. Если всё правильно, в статус-баре внизу появится ваше имя пользователя GitLab.</p><h2>Шаг 4. Работаем с проектом</h2><p>Попробуем клонировать проект. Нажмите Ctrl+Shift+P и выберите «GitLab: Clone from GitLab». Расширение покажет список доступных проектов. Выберите нужный, укажите папку для клонирования.</p><p>Теперь вам доступны функции Git:</p><ul><li><b>Pipeline </b>— в боковой панели GitLab видны все запущенные сборки, их статусы и логи.</li><li><b>Issues </b>— создавайте, редактируйте и закрывайте задачи прямо из редактора.</li><li><b>Merge requests</b> — посмотрите список MR, оставьте комментарии к коду, одобрите изменения.</li><li><b>Snippets </b>— сохраняйте фрагменты кода для повторного использования.</li></ul><h2>Как подключить VSCode к Docker</h2><p>Редактор умеет запускать код прямо внутри контейнера. Вы редактируете файлы, а выполняются они в изолированной среде с выбранными настройками.</p><h2>Шаг 1. Подготовка</h2><p>Установите Docker Desktop с <a href="https://www.docker.com/products/docker-desktop/">официального сайта</a>. После установки убедитесь, что Docker запущен — в системном трее должна появиться иконка кита.</p><p>В VSCode установите два расширения:</p><ul><li>Docker.</li><li>Dev Containers.</li></ul><h2>Шаг 2. Создаём Dockerfile</h2><p>В корне проекта создайте файл Dockerfile. Пример для Python-проекта:</p><p>Этот файл говорит Docker:</p><ol><li>Возьми базовый образ Python 3.11.</li><li>Создай рабочую директорию /app.</li><li>Скопируй и установи зависимости.</li><li>Скопируй весь код проекта.</li><li>При запуске выполни команду python main.py.</li></ol><h2>Шаг 3. Настраиваем Dev Container</h2><p>Создайте папку .devcontainer в корне проекта. Внутри создайте файл devcontainer.json:</p><p>Конфигурация использует ваш Dockerfile для сборки контейнера. Она автоматически устанавливает Python-расширения внутри контейнера, пробрасывает порт 8000 (если у вас веб-приложение) и выполняет команду после создания контейнера.</p><h2>Шаг 4. Запускаем разработку в контейнере</h2><p>Нажмите Ctrl+Shift+P и выберите «Dev Containers: Reopen in Container». Редактор соберёт Docker-образ по вашему Dockerfile, запустит контейнер, подключится к нему и откроет ваш проект уже внутри контейнера.</p><p>Теперь весь код выполняется в изолированной среде. Терминал в VSCode работает внутри контейнера. Расширения устанавливаются в контейнер, отладчик запускает код там же.</p><h2>Как подключить VSCode к Jupyter</h2><p>В классическом интерфейсе нет нормального автодополнения кода, сложно работать с Git, неудобно рефакторить код и нет полноценной отладки. Плагины VSCode решают эти проблемы.</p><h2>Шаг 1. Установка</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-11-05/eb48ea48-285c-4f04-90c2-e20932174fe9.jpg" alt="" /></figure><p>Установите расширение «Jupyter» от Microsoft. Оно автоматически подтянет все необходимые зависимости.</p><p>В терминале установите Jupyter и ipykernel:</p><h2>Шаг 2. Создаём первый ноутбук</h2><p>Создайте файл с расширением .ipynb или нажмите Ctrl+Shift+P &gt;&gt; «Create: New Jupyter Notebook».</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-11-05/f956c803-cb87-4a4e-b849-7f4b956fb2e8.jpg" alt="" /></figure><p>VSCode откроет интерфейс ноутбука. Вверху вы увидите панель инструментов с выбором ядра, кнопки запуска и добавления ячеек.</p><h2>Шаг 3. Выбираем ядро</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-11-05/d66667d6-f595-494f-a0ef-a9480b1b9da8.jpg" alt="" /></figure><p>Кликните на «Select Kernel» в правом верхнем углу. VSCode покажет список доступных интерпретаторов:</p><ul><li>Локальные Python-окружения.</li><li>Виртуальные окружения (venv, conda).</li><li>Docker-контейнеры (если настроены).</li><li>Удалённые Jupyter-серверы.</li></ul><p>Выберите нужное окружение. VSCode запомнит выбор для этого ноутбука.</p><h2>Шаг 4. Пишем и выполняем код</h2><p>В ячейке напишите код:</p><p>Нажмите Shift+Enter для выполнения ячейки. График отобразится прямо под кодом.</p><p>Для Python-файлов можно включить интерактивный режим. Добавьте комментарий <i># %%</i> в .py файл:</p><p>VSCode покажет кнопки «Run Cell» над каждым блоком. Теперь можно выполнять код частям.</p><h2>Что в итоге</h2><p>Первая настройка VSCode с GitLab, Docker и Jupyter занимает 30-40 минут. В итоге вы получаете единое рабочее пространство для ваших задач разработки.</p><p>Начните с GitLab и научитесь создавать merge requests из редактора. Потом добавьте Docker для одного проекта. Когда освоитесь, подключите Jupyter для работы с данными. Инструменты решают разные задачи, а VSCode объединяет их функции в одном интерфейсе.</p><p>Возьмите проект и последовательно подключите каждый инструмент. Через неделю удивитесь, как раньше работали без этой связки. Код пишется быстрее, ошибок меньше, а переключений между окнами почти нет.</p><p><i>Если что-то непонятно или не получается настроить — пишите в комментариях, разберёмся вместе!</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Как мы автоматизировали мутационное тестирование unit тестов на проекте в крупном банке с использованием Stryker.NET</title>
      <link>https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net</link>
      <comments>https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[asyncguru]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net</guid>
      <description><![CDATA[<p>Опыт автоматизации мутационного тестирования Unit-тестов в крупном банке с помощью Stryker.NET. Практический кейс по внедрению в CI/CD, настройке и интеграции в legacy-проект. Как мы нашли слабые места в тестах и повысили их надёжность, не замедляя процесс разработки.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net">Как мы автоматизировали мутационное тестирование unit тестов на проекте в крупном банке с использованием Stryker.NET</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[.NET]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 11 Oct 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Качественные модульные тесты помогают оптимизировать ресурсы команды разработки и увеличивают надежность создаваемого продукта. Оценить эффективность этих тестов можно с помощью автоматизированного инструмента для мутационного тестирования Stryker.NET.</p><p><i>Меня зовут Юрий Каган, я Software/System Architect в IT_ONE. Расскажу об опыте применения </i><i>Stryker.NET </i><i>на проекте в крупном банке. </i><i></i></p><h2>Качественные юнит-тесты —  основа надежного ПО</h2><p>Несмотря на то, что юнит-тесты —  только один из компонентов пирамиды тестирования ПО, они —  база для создания качественного кода:</p><p>– Благодаря проверке отдельных фрагментов кода можно <b>выявить дефекты на ранних стадиях</b>, не пропуская их на следующие этапы разработки. Известно, что чем раньше обнаружена ошибка, тем ниже стоимость её исправления.</p><p>– Модульные тесты обеспечивают <b>стабильность разработки</b>, служа некой страховкой: они предотвращают регрессии при изменениях и рефакторинге.</p><p>– Проведённые тесты служат документацией и <b>ускоряют внедрение нового функционала</b>. По ним можно понять изначальный замысел автора кода и упростить разработку.</p><p>Для оценки качества юнит-тестов разработчики обычно пользуются метрикой Code Coverage, отражающей процент покрытия тестами исходного кода. Такая информация может собираться различными способами в зависимости от типа используемого инструмента, но результат получается схожий: данные о том, какие конкретно фрагменты кода выполнялись во время тестов. О каких бы то ни было проверках речи не идет. То есть, по нашему опыту, технически очень легко можно написать тесты, которые будут давать 100% Code Coverage и при этом ничего не проверять. Это означает, что даже высокий процент покрытия кода тестами не гарантирует, что они разработаны качественно и поведение вашего кода надёжно зафиксировано. После них нельзя исключать скрытые ошибки.</p><p>Как следствие, постоянно осознавая риски некачественных тестов, разработчики могут начать бояться рефакторинга —  ведь любые изменения потенциально грозят дефектами в коде. Причем допущенный дефект может быть обнаружен намного позже, уже в продакшене. Всё это приводит к дополнительным рискам, замедляет и удорожает цикл разработки.</p><p>Но существует и другая, не столь широко известная метрика, которая более объективно описывает надёжность проведённых тестов: она определяется в процессе <b>мутационного тестирования</b>. Первые упоминания об этом подходе мы встречаем еще в 1970-х годах. Тогда он уже признавался перспективным, но в то же время —  практически неприменимым из-за запредельного объема работы. Сегодня мы имеем возможность автоматизировать большую часть этой работы с помощью инструментов, например, Stryker.NET.</p><h2>Принципы мутационного тестирования</h2><p>Алгоритм мутационного тестирования достаточно прост: в исходный код (базу) вносятся различные изменения (мутации). Существует несколько разновидностей мутаций. Основные из них:</p><p>– <b>изменение операторов</b>: замена арифметических и логических операторов (например, + на -, &gt;= на &gt;),</p><p>– <b>изменение значений</b>: замена булевых значений, удаление вызовов методов или изменение литералов,</p><p>– <b>изменение условий</b>: в if, циклах и логических выражениях.</p><p>Затем на этом модифицированном коде выполняются модульные тесты для проверки их чувствительности к изменениям. Мутанты, которые вызывают провал тестов, считаются «убитыми» (Killed Mutants), остальные —  «выжившими». По итогу проверки формируется отчет, где ключевая метрика качества тестов —  Mutation Score —  доля убитых мутантов от их общего количества. Если тесты не «убивают» подавляющее число мутантов, значит их нельзя считать достаточно эффективными.</p><p>Среди инструментов для автоматизации проведения мутационного тестирования мы остановили выбор на Stryker.NET и вот, почему:</p><p>– на данный момент Stryker.NET обладает <b>наибольшим объемом автоматизированных операций</b>: он самостоятельно вносит мутации в исходный код, запускает юнит-тесты и генерирует подробные отчеты;</p><p>– Stryker.NET <b>полностью интегрирован с .NET-экосистемой</b>: поддерживает .NET и .NET Framework, основные тестовые фреймворки (xUnit, NUnit, MSTest);</p><p>– Stryker.NET – это бесплатный <b>инструмент с открытым исходным кодом</b>, постоянно обновляемый и поддерживаемый сообществом, что гарантирует его актуальность и развитие.</p><p>По нашей практике, Stryker.NET будет полезен для трех категорий пользователей:</p><p>– <b>Разработчик</b> может убедиться, что новый код покрыт качественными тестами и изменения не привели к деградации существующих тестов, а также находить и удалять тесты, которые ничего не тестируют и только отнимают ресурсы.</p><p>– <b>Ревьюер</b> может быстро и надежно проверить качество тестов в pull request.</p><p>– <b>ИТ-архитектор и руководитель разработки</b> могут регулярно строить и анализировать отчеты, чтобы мониторить «здоровье» юнит-тестов во всей кодовой базе.</p><h2>Установка и использование Stryker.NET</h2><p>Технически Stryker.NET представляет из себя dotnet tool. Соответственно, чтобы его установить, необходимо выполнить команду в cmd или в PowerShell консоли:</p><p><i>dotnet tool install -g dotnet stryker</i></p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/29e01f48-c311-41e1-a22b-b0be87e83f32.png" alt="" /><figcaption>установка Stryker.NET</figcaption></figure><p>Получение и анализ отчетов: результаты выводятся в консоль и сохраняются в подробном HTML-отчете.</p><p>Stryker.NET поддерживает несколько сценариев, простейший из них — анализ проекта с кодом. В этом случае необходимо запустить мутационное тестирование из папки, где расположен файл проекта, указать имя этого файла без пути и через ключ <b><i>tp</i></b> указать полные пути ко всем файлам проектов, содержащим тесты для проекта с кодом. Например:</p><p><i>dotnet stryker -p “project.csproj” -tp “c:\git\project.tests\project.tests.csproj”</i></p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/19df0a95-2009-4c70-b05b-c9fc49c9da8c.png" alt="" /><figcaption>запуск Stryker.NET для анализа проекта с кодом</figcaption></figure><p>В результате тестирования программа сгенерирует отчет, где в первой колонке будет выведена метрика Mutation Score в целом по проекту и по отдельным файлам, причем разделённая на две группы: <i>Of total</i> —  общее количество мутантов, <i>Of covered</i> —  количество мутантов, находящихся в тех фрагментах кода, для которых вообще существуют тесты.</p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/2d7165e7-c7b8-4f67-ac0a-bcbab2514419.png" alt="" /><figcaption>детальный отчет Stryker.NET файлов проекта, метрики</figcaption></figure><p>В колонке <i>Killed</i> отображается количество убитых мутантов, в колонке <i>Survived</i> —  количество выживших. В колонке <i>Timeout</i> —  количество мутантов, которые привели к зацикливанию выполнения тестов и их прерыванию утилитой. Значения остальных колонок, а также другую важную информацию можно посмотреть в документации на официальном сайте <a href="https://stryker-mutator.io/docs/stryker-net/introduction/">https://stryker-mutator.io/docs/stryker-net/introduction/</a>.</p><p>Также важно отметить, что отчёт доступен для более глубокого и детализированного анализа: можно раскрыть параметры каждого файла и увидеть подробную информацию о том, какие именно изменения были произведены, какие из мутантов выжили и почему.</p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/c2f8db51-6aaa-4dc9-8fa2-10bee3457fef.png" alt="" /><figcaption>красная точка показывает часть кода с выжившим мутаном</figcaption></figure><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/e1b90d3b-d5fa-44e9-b39e-7c7e847a9287.png" alt="" /><figcaption>кликнув на красную точку, можно увидеть, какая именно мутация выжила</figcaption></figure><p>Stryker.NET умеет генерировать отчеты не только в HTML, но и в других форматах: например, в JSON, который очень удобен для автоматического анализа, если разработчики планируют встроить этот инструмент в свой CI/CD-пайплайн. Кроме того, есть встроенный дашборд, который, к сожалению, невозможно развернуть локально —  он доступен только как online сервис (https://dashboard.stryker-mutator.io).</p><h2>Применение Stryker.NET при разработке банковских продуктов</h2><p>Мы внедрили использование Stryker.NET в проекте для крупного банка, чтобы с помощью этого инструмента решить <b>ряд взаимосвязанных задач</b>. Самая главная проблема заключалась в том, что мы хотели улучшить качество тестов посредством мутационного тестирования, но его проведение вручную —  предельно утомительная и требующая много времени процедура. Ни Product Owners, ни разработчики не были готовы постоянно выделять на это ресурсы.</p><p>Без регулярного мутационного тестирования, нам было практически невозможно оценить текущее состояние всей кодовой базы с точки зрения качества unit тестов. У нас было много unit тестов, мы имели высокий процент Code Coverage, но не знали, насколько эти тесты нас защищают. В свою очередь, без понимания текущего состояния у нас не было возможности устанавливать команде цели по улучшению ситуации.</p><p>На данный момент команда активно <b>использует Stryker.NET на всех этапах разработки</b>.</p><p>Мы столкнулись и с ограничением использования Stryker.NET: попытка его интеграции в CI/CD-пайплайн оказалась неудачной. Это связано с тем, что кодовая база проекта насчитывает несколько миллионов строк, и выполнение всех проверок занимает примерно 12 часов. Однако мы думаем, что на проектах меньшего размера такая интеграция должна сработать.</p><p>Мы выбрали альтернативный вариант: был внедрен регулярный автоматический пост-релизный прогон Stryker.NET по ветке master, в результате которого генерируется сводный отчет. Проводится анализ изменений сводного отчета по всем проектам от релиза к релизу. По данным каждого сводного отчета мы можем оценивать работу конкретных проектных команд, и если их метрика Mutation Score недостаточно высока, – ставить цели по улучшению.</p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/cb80b102-c01a-4ed2-8892-af8630d0281f.png" alt="" /><figcaption>сводный отчет по всем проектам solution'на</figcaption></figure><p><b>Резюме</b><b></b></p><p>Итак, мутационное тестирование – мощный инструмент для повышения качества юнит-тестов и, соответственно, качества кода. Оно позволяет не только узнать, какой процент кода покрыт тестами, но и убедиться в том, что эти тесты действительно защищают код от ошибок.</p><p>Внедрение Stryker.NET —  шаг к более надёжной и предсказуемой разработке. Его регулярное использование помогает уверенно вносить изменения, рефакторить код и добавлять новый функционал.</p><p>Мы рекомендуем начать использование Stryker.NET в ваших проектах с ключевых модулей. При этом стоит постоянно делиться опытом с командой, чтобы наиболее эффективно улучшать программный продукт.</p>]]></content:encoded>
    </item>
    <item>
      <title>OpenAI тестирует Agent Builder: теперь свой ИИ сможет собрать каждый</title>
      <link>https://tproger.ru/news/openai-testiruet-agent-builder--teper-svoj-ii-smozhet-sobrat-kazhdyj</link>
      <comments>https://tproger.ru/news/openai-testiruet-agent-builder--teper-svoj-ii-smozhet-sobrat-kazhdyj?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/openai-testiruet-agent-builder--teper-svoj-ii-smozhet-sobrat-kazhdyj</guid>
      <description><![CDATA[<p>OpenAI тестирует Agent Builder — визуальный конструктор ИИ-агентов, с которым можно собирать сложные сценарии без лишнего кода. Поддержка Gmail, Google Drive и других сервисов делает инструмент мощным конкурентом существующим платформам автоматизации. Подробности — на DevDay.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/openai-testiruet-agent-builder--teper-svoj-ii-smozhet-sobrat-kazhdyj">OpenAI тестирует Agent Builder: теперь свой ИИ сможет собрать каждый</a>»</p>]]></description>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 08 Oct 2025 09:10:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>OpenAI делает ещё один шаг к тому, чтобы ИИ стал не просто умным собеседником, а полноценным рабочим инструментом. Компания <a href="https://www.techspot.com/news/109770-streamer-completes-record-breaking-minecraft-walk-after-145.html">тестирует</a> Agent Builder — конструктор, в котором можно собрать своего ИИ-агента почти как из LEGO. Минимум кода, максимум логики.</p><h2>Подробности</h2><p>Первые скриншоты нового инструмента <a href="https://x.com/testingcatalog/status/1974934915474137554">появились</a> в X. Интерфейс напоминает визуальные редакторы: вы берёте блоки (nodes), соединяете их стрелками и определяете последовательность действий. Можно стартовать с шаблона вроде «Customer service», «Document comparison» или «Data enrichment» — или собрать всё с нуля.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-10-08/48268c8f-2d4f-4d04-98b8-08a89c0ed193.png" alt="" /></figure><p>Каждый блок отвечает за отдельную задачу, а все они связаны с агентом, которому можно задать правила работы: выбрать модель, прописать подсказки, определить формат вывода (текст или JSON) и даже задать уровень «усилий на рассуждение».</p><p>Agent Builder также поддерживает MCP — это значит, что к агенту можно подключить Gmail, Google Calendar, Google Drive, Outlook, Teams, SharePoint или Dropbox. После подключения он сможет, например, читать файлы, доставать события из календаря или черновики писем.</p><p>Подробнее OpenAI расскажет о проекте на конференции DevDay, которая пройдёт сегодня. AI-сообщество уже окрестило этот релиз «Visual Studio моментом» для мира ИИ — настолько простым и гибким инструмент может стать для разработчиков.</p>]]></content:encoded>
    </item>
    <item>
      <title>Руководство по промптам GPT-5: практики для агентов, кодирования и управляемости</title>
      <link>https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti</link>
      <comments>https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Даровская Маша]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti</guid>
      <description><![CDATA[<p>Руководство по промптам GPT-5: практики для агентов, кодирования и управляемости от OpenAI. Готовые шаблоны промптов, настройка reasoning_effort, работа с Responses API и советы по созданию приложений. Повысьте стабильность и эффективность ваших ИИ-решений.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti">Руководство по промптам GPT-5: практики для агентов, кодирования и управляемости</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Markdown]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Промпты]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 07 Oct 2025 14:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Мы перевели <a href="https://cookbook.openai.com/examples/gpt-5/gpt-5_prompting_guide">статью</a> Ануп Кота, Джулиан Ли, Эрика Закариассон из OpenAI. Статья написана для разработчиков агентных систем, инженеров ИИ-продуктов, команд фронтенда/бэкенда, редакторов кода с ИИ.</p><p>GPT-5 — флагманская reasoning-модель с упором на агентные сценарии, кодинг, интеллект и управляемость. «Из коробки» она хорошо решает широкий спектр задач, но качественные промпты (подсказки) заметно повышают стабильность, скорость и соблюдение инструкций. Ниже — проверенные практики, готовые шаблоны и заметки по параметрам API (reasoning_effort, verbosity, Responses).</p><h2>Предсказуемость агентного рабочего процесса</h2><h2>Используйте API Responses для агентов</h2><p>Responses сохраняет логические следы между вызовами инструментов: это снижает задержку, экономит токены и повышает качество планов за счёт механизма previous_response_id.</p><h2>Контролируйте «рвение» агента</h2><p>Сдержанный режим (меньше вызовов, ниже задержки):</p><ul><li>Установите reasoning_effort=low|medium.</li><li>В подсказке ограничьте глубину поиска контекста и задайте ранние критерии остановки.</li></ul><h2>Проактивный режим (больше автономии и настойчивости):</h2><ul><li>Поднимите reasoning_effort.</li><li>Включите явное требование «не возвращаться к пользователю до полного решения».</li></ul><p>Если вы готовы к максимально строгому регулированию, то можете установить фиксированный бюджет на вызовы инструментов, как показано ниже. Бюджет, естественно, может варьироваться в зависимости от желаемой глубины поиска.</p><p>При ограничении основного поведения сбора контекста полезно явно предоставить модели запасной вариант, облегчающий выполнение более короткого этапа сбора контекста. Обычно это делается в виде условия, позволяющего модели продолжать работу в условиях неопределенности, как «even if it might not be fully correct» в примере выше.</p><p>С другой стороны, если вы хотите поощрить автономность модели, увеличить настойчивость в вызове инструментов и сократить количество уточняющих вопросов или иных случаев возврата информации пользователю, рекомендуется увеличить reasoning_effort и использовать такой промпт, чтобы поощрить настойчивость и тщательное выполнение задачи:</p><p>Хорошая практика — чётко указать условия остановки задач агента, обозначить безопасные и небезопасные действия и определить, когда, если это вообще возможно, модель может вернуть данные пользователю. Например, в наборе инструментов для покупок инструменты оформления заказов и оплаты должны явно иметь более низкий порог неопределённости, требующий пояснений пользователя. В то же время инструмент поиска должен иметь чрезвычайно высокий порог; аналогично, в конфигурации кодинга инструмент удаления файлов должен иметь гораздо более низкий порог, чем инструмент поиска grep.</p><h2>«Преамбулы» к инструментам (чтобы пользователь понимал, что происходит)</h2><p>GPT-5 обучен предоставлять чёткие предварительные планы и последовательные обновления о ходе работы с помощью сообщений «преамбулы инструмента».</p><p>Вы можете управлять частотой, стилем и содержанием преамбул инструментов в вашем запросе — от подробных объяснений каждого вызова инструмента до краткого предварительного плана. Вот пример качественной преамбулы:</p><p>Вот пример преамбулы инструмента, которая может быть выведена в ответ на такой запрос. Такие преамбулы могут значительно улучшить способность пользователя следить за работой вашего агента по мере её усложнения:</p><h2>Усилия рассуждения и повторное использование контекста</h2><ul><li>reasoning_effort регулирует интенсивность размышлений и готовность к вызову инструментов. Значение по умолчанию — medium. Но для многошаговых задач повышайте этот показатель.</li><li>Разбивайте сценарий на несколько ходов агента с сохранением previous_response_id в Responses — модель не тратит токены на перестройку плана.</li><li>Настоятельно рекомендуют использовать API Responses в GPT-5, чтобы улучшить потоки агентов, снизить затраты и повысить эффективность токенов в приложениях. Авторы отмечают, что наблюдали статистически значимые улучшения в оценках при использовании API Responses по сравнению с завершением чата. Например, рост оценки Tau-Bench Retail с 73,9% до 78,2% был только благодаря переходу на API Responses и включению previous_response_id (функции передачи предыдущих элементов рассуждений в последующие запросы). Это позволяет модели ссылаться на предыдущие трассировки рассуждений, экономя токены и устраняя необходимость перестраивать план с нуля после каждого вызова инструмента, что снижает latency. Эта функция доступна всем пользователям API Responses.</li></ul><h2>Максимизация продуктивности в кодинге</h2><p>GPT-5 умеет работать с крупными кодовыми базами, накатывать многофайловые изменения, рефакторить и строить новые приложения.</p><h2>Рекомендованный стек для фронтенд-приложений</h2><ul><li>Фреймворк: Next.js (TypeScript), React, HTML</li><li>Стили/UI: Tailwind CSS, shadcn/ui, Radix themes</li><li>Иконки: Lucide / Heroicons / Material Symbols</li><li>Анимации: Framer Motion</li><li>Шрифты: Inter, Geist, Mona Sans, IBM Plex Sans, Manrope</li></ul><h2>Разработка приложений с нуля</h2><p>GPT-5 отлично подходит для создания приложений за один раз. В ходе ранних экспериментов с моделью пользователи обнаружили, что подсказки, подобные приведённой ниже, — где модель итеративно выполняет задания, используя самостоятельно разработанные критерии качества, — повышают качество результатов благодаря использованию возможностей GPT-5 в области тщательного планирования и самоанализа.</p><h2>Соответствие стандартам разработки</h2><p>При внедрении постепенных изменений и рефакторинга в приложения, код, написанный на основе модели, должен соответствовать стандартам стиля и дизайна и максимально аккуратно вписываться в кодовую базу. Без специальных подсказок GPT-5 автоматически ищет справочный контекст в кодовой базе, например, читая package.json для просмотра уже установленных пакетов. Но это поведение можно улучшить с помощью подсказок, обобщающих ключевые аспекты, такие как принципы разработки, структура каталогов и передовой опыт кодовой базы.</p><p>Фрагмент подсказки ниже демонстрирует один из способов организации правил редактирования кода для GPT-5: не стесняйтесь изменять фактическое содержание правил в соответствии со своими предпочтениями в программном дизайне.</p><h2>Форматирование Markdown</h2><p>По умолчанию GPT-5 в API не форматирует свои окончательные ответы в Markdown, чтобы обеспечить максимальную совместимость с разработчиками, чьи приложения могут не поддерживать рендеринг Markdown. Тем не менее, запросы, подобные следующему, в значительной степени успешно обеспечивают иерархические окончательные ответы в Markdown.</p><p>Иногда соблюдение инструкций Markdown, указанных в системном запросе, может ухудшаться в течение длительного разговора. Если вы столкнулись с такой ситуацией, стабильное соблюдение инструкций Markdown будет работать при добавлении их к каждому 3–5 пользовательскому сообщению.</p>]]></content:encoded>
    </item>
    <item>
      <title>Вышел NWinfo 1.4.4: открытый инструмент для быстрого просмотра характеристик ПК на Windows</title>
      <link>https://tproger.ru/news/vywel-nwinfo-1-4-4--otkrytyj-instrument-dlya-bystrogo-prosmotra-harakteristik-pk-na-windows</link>
      <comments>https://tproger.ru/news/vywel-nwinfo-1-4-4--otkrytyj-instrument-dlya-bystrogo-prosmotra-harakteristik-pk-na-windows?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/vywel-nwinfo-1-4-4--otkrytyj-instrument-dlya-bystrogo-prosmotra-harakteristik-pk-na-windows</guid>
      <description><![CDATA[<p>Вышел релиз NWinfo 1.4.4 — портативного open source инструмента для просмотра характеристик ПК на Windows. Новая версия получила поддержку Ctrl+C, отчёты в JSON/YAML/LUA и обновлённые драйверы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/vywel-nwinfo-1-4-4--otkrytyj-instrument-dlya-bystrogo-prosmotra-harakteristik-pk-na-windows">Вышел NWinfo 1.4.4: открытый инструмент для быстрого просмотра характеристик ПК на Windows</a>»</p>]]></description>
      <category><![CDATA[C#]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Процессор]]></category>
      <category><![CDATA[Hardware]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 06 Oct 2025 15:40:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В начале октября 2025 года <a href="https://github.com/a1ive/nwinfo/releases/tag/v1.4.4">вышла новая версия</a> NWinfo 1.4.4 — лёгкого портативного инструмента для просмотра ключевых характеристик компьютера: от процессора и памяти до сетевых адаптеров и дисков. Программа работает на Windows, начиная с XP, и написана на языке C.</p><p>NWinfo позволяет мгновенно получить сводку по системе — включая данные о ЦП, ОЗУ, SMBIOS, CPUID, S.M.A.R.T., PCI, EDID, видеокартах и сетевых интерфейсах. Утилита не требует установки и может запускаться с любого носителя, что делает её удобным решением для диагностики и устранения неполадок. Также поддерживается экспорт отчётов в JSON, YAML и LUA, что облегчает обмен данными и автоматизацию.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-10-06/00ce5af3-c800-4aae-9869-d371448273a4.png" alt="" /></figure><p>В версии 1.4.4 разработчики исправили ряд ошибок, добавили поддержку копирования через Ctrl+C в графическом интерфейсе, внедрили отображение информации о целостности кода (Code Integrity) и обновили драйверы. Также появилась поддержка PawnIO, расширяющая взаимодействие с файловыми системами.</p><p>NWinfo часто сравнивают с другими инструментами для анализа аппаратной части ПК, но это не аналог HWiNFO. Последний, напомним, обновился до версии 8.30 в августе 2025 года и используется для системного мониторинга и диагностики. Похожий по функциональности проект Glow 25.12, выпущенный в сентябре 2025 года, предлагает детальный анализ компонентов в интерфейсе на C# под лицензией MIT.</p><p>Релиз NWinfo 1.4.4 подтверждает растущий интерес к лёгким и прозрачным инструментам для системного анализа, которые не требуют установки и уважают приватность пользователя.</p>]]></content:encoded>
    </item>
    <item>
      <title>10 VSCode расширений, которые реально повышают продуктивность</title>
      <link>https://tproger.ru/articles/10-vscode-raswirenij--kotorye-realno-povywayut-produktivnost</link>
      <comments>https://tproger.ru/articles/10-vscode-raswirenij--kotorye-realno-povywayut-produktivnost?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/10-vscode-raswirenij--kotorye-realno-povywayut-produktivnost</guid>
      <description><![CDATA[<p>Топ-10 расширений VSCode для повышения продуктивности: форматирование, тестирование API, управление проектами и многое другое. Ускорьте свою разработку с лучшими инструментами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/10-vscode-raswirenij--kotorye-realno-povywayut-produktivnost">10 VSCode расширений, которые реально повышают продуктивность</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[SEO]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[BASIC]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Intel]]></category>
      <category><![CDATA[ICO]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Английский]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 23 Sep 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Visual Studio Code — мощный редактор, который становится ещё лучше с правильными расширениями. В этой подборке мы собрали 10 инструментов, реально ускоряющих разработку: они избавляют от рутины и помогают сосредоточиться на главном — качественном коде.</p><h2>1. TabNine</h2><p>Если вам важна скорость — <a href="https://www.tabnine.com/">TabNine</a> стоит попробовать хотя бы ради этого. Расширение —  лёгкий AI-ассистент, который работает как умный автодополнитель кода, непохожий на аналоги вроде Codeium или Copilot.</p><p>TabNine обучен на миллионах строк открытого кода и умеет предсказывать, что вы напишете дальше, с учётом контекста проекта и языка. Отлично справляется с рутинными вещами: автозавершает функции, переменные, конструкции — всё быстро и чаще всего в тему.</p><p><b>Кому подойдёт</b>: тем, кто хочет ускорить набор кода, но не готов передавать весь проект в облако или открывать чат с Copilot. Поддерживает офлайн-режим и локальное обучение модели.</p><p>Плюсы:</p><ul><li>Работает из коробки, не требует тонкой настройки;</li><li>Есть локальная версия — удобно для закрытых проектов;</li><li>Поддерживает большинство языков и фреймворков.</li></ul><p><b>Чем полезен:</b> экономит время на повседневной разработке — особенно когда вы не хотите отвлекаться на документацию или поиск нужной переменной в другом файле.</p><h2>2. GitLens</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens">GitLens</a> встраивает историю изменений прямо в VSCode — и делает это максимально удобно. Показывает, кто и когда изменил строку, коммит-месседж, хэш, ветку и другие детали. Можно не уходить в консоль или отдельный Git-клиент для поиска информации.</p><p>Особенно полезен, когда работаете с чужим кодом или хотите быстро вспомнить, зачем вы сами что-то написали месяц назад.</p><p><b>Кому подойдёт</b>: тем, кто работает в команде, часто читает историю изменений или ревьюит чужие коммиты. Также пригодится в проектах с долгой историей или нестабильным кодом.</p><p>Плюсы:</p><ul><li>Информация о коммитах отображается прямо в редакторе под строкой;</li><li>Есть таймлайн изменений файла;</li><li>Удобный diff по коммитам, авторам, веткам и даже фрагментам кода.</li></ul><p><b>Чем полезен:</b> помогает быстрее разбираться в чужом коде, искать причины бага или откатывать ошибки. Особенно ценится за то, что делает Git прозрачным и доступным прямо в процессе разработки.</p><h2>3. Error Lens</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens">Error Lens</a> выводит диагностику ошибок и предупреждений прямо в строке кода. Это расширение превращает сообщения линтеров и компиляторов в наглядные подсказки, позволяя сразу видеть, что пошло не так.</p><p>Можно настроить отображение: выделять ошибки цветом, добавлять иконки или даже показывать краткие подсказки с предложениями по исправлению. Поддерживает большинство языков и линтеров, включая ESLint, TypeScript и других.</p><p><b>Кому подойдёт:</b> разработчикам, которые хотят моментально видеть ошибки в коде, и тем, кто ценит визуальную чистоту и скорость отладки.</p><p>Плюсы:</p><ul><li>Ошибки и предупреждения отображаются прямо в редакторе, рядом со строкой кода;</li><li>Гибкая настройка стилей и уровня детализации сообщений;</li><li>Ускоряет процесс отладки, особенно при работе с большими файлами.</li></ul><p><b>Чем полезен:</b> минимизирует время на поиск и анализ ошибок, позволяя сразу фокусироваться на их исправлении. Это особенно ценно, когда вы пишете код в реальном времени или работаете с новыми библиотеками, где легко допустить мелкие недочёты.</p><h2>4. Path Intellisense</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=christian-kohler.path-intellisense">Path Intellisense</a> — одно из тех расширений, которое просто работает и экономит кучу времени. Оно автоматически подсказывает пути к файлам, папкам и модулям в вашем проекте, как только вы начинаете их набирать. Поддерживает абсолютные и относительные пути, учитывает структуру проекта и форматирует предложения так, как это принято в выбранном языке.</p><p>Работает особенно хорошо в проектах с вложенной структурой, когда нужно быстро сослаться на компоненты, конфиги или ассеты.</p><p><b>Кому подойдёт</b>: всем, кто устал вручную прописывать длинные relative-пути или путаться в структуре проекта. Особенно выручает на фронтенде, где модулей десятки и легко ошибиться в названии.</p><p>Плюсы:</p><ul><li>Подсказки появляются автоматически при наборе пути;</li><li>Поддерживает большинство языков и фреймворков;</li><li>Учитывает jsconfig.json и tsconfig.json при работе с alias-ами.</li></ul><p><b>Чем полезен</b>: снижает количество опечаток и неверных импортов, ускоряет переход между файлами и помогает писать код чуть быстрее.</p><h2>5. TODO Highlight</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight">TODO Highlight</a> подсвечивает комментарии с задачами (например, TODO, FIXME, NOTE) прямо в коде, делая их заметными и удобными для отслеживания. Расширение помогает не терять важные заметки, которые вы оставляете в коде, и быстро находить места, требующие доработки.</p><p>Можно настроить ключевые слова, цвета подсветки и даже добавить свои собственные метки. Работает с любыми языками программирования и интегрируется с панелью задач VSCode для удобного обзора всех TODO в проекте.</p><p><b>Кому подойдёт</b>: разработчикам, которые оставляют заметки в коде, и командам, которым нужно быстро находить задачи или недочёты в проекте.</p><p>Плюсы:</p><ul><li>Яркая подсветка TODO-комментариев прямо в редакторе;</li><li>Гибкая настройка ключевых слов и стилей;</li><li>Интеграция с панелью задач для быстрого обзора.</li></ul><p><b>Чем полезен</b>: экономит время на поиск и управление задачами в коде, помогая не упустить важные доработки или напоминания. Особенно удобно в больших проектах, где комментарии могут затеряться среди строк.</p><h2>6. Prettier – Code formatter</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode">Prettier</a> — расширение, которое автоматически приводит ваш код к единому стилю, избавляет от ручной правки отступов, кавычек и переносов. Оно поддерживает множество языков (JavaScript, TypeScript, CSS, HTML и другие) и интегрируется с линтерами, чтобы ваш код был не только красивым, но и консистентным.</p><p>Можно настроить правила форматирования под ваш проект или использовать готовые пресеты. Prettier форматирует код при сохранении файла или по команде, а также работает с выделенными фрагментами.</p><p><b>Кому подойдёт</b>: разработчикам, которые хотят экономить время на форматировании, и командам, стремящимся к единообразию кода.</p><p>Плюсы:</p><ul><li>Автоматическое форматирование при сохранении или по хоткеям;</li><li>Поддержка множества языков и кастомных настроек;</li><li>Интеграция с ESLint и другими инструментами для проверки кода.</li></ul><p><b>Чем полезен:</b> убирает рутину ручного форматирования, снижает количество ошибок в стиле кода и помогает сосредоточиться на логике, а не на внешнем виде. Идеально, чтобы ускорить работу и поддерживать чистоту в больших проектах.</p><h2>7. Live Server</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer">Live Server </a>запускает локальный сервер прямо из VSCode, позволяя просматривать изменения в HTML, CSS и JavaScript в браузере в реальном времени. После сохранения файла страница автоматически обновляется, что исключает необходимость ручного перезапуска или обновления браузера.</p><p>Поддерживает кастомные порты, HTTPS, и работает с любыми фронтенд-проектами, от простых HTML-страниц до сложных приложений на React или Vue. Можно настроить, чтобы сервер открывался автоматически при запуске проекта.</p><p><b>Кому подойдёт</b>: фронтенд-разработчикам, которые работают над веб-интерфейсами и хотят мгновенно видеть результат изменений без лишних действий.</p><p>Плюсы:</p><ul><li>Автоматическое обновление страницы при изменении кода;</li><li>Простая настройка и поддержка HTTPS для безопасного тестирования;</li><li>Лёгкий запуск сервера прямо из редактора.</li></ul><p><b>Чем полезен</b>: ускоряет цикл разработки и тестирования веб-приложений, избавляет от ручного обновления страниц. Это особенно экономит время при частых правках в стилях или скриптах, так как можно сразу видеть результат в браузере.</p><h2>8. Project Manager</h2><p>Если работаете над несколькими проектами одновременно — это расширение сэкономит вам часы. <a href="https://marketplace.visualstudio.com/items?itemName=alefragnani.project-manager">Project Manager</a> позволяет создавать список избранных проектов и открывать их в один клик, без ручного поиска папок и недавних вкладок.</p><p>Можно задать свои алиасы, группировать по папкам, запускать с хоткеев — особенно удобно, если у вас десятки репозиториев на локалке или вы фрилансите на несколько команд.</p><p><b>Кому подойдёт</b>: разработчикам, которые ведут сразу несколько проектов, часто переключаются между ними и устали искать нужный путь через File &gt; Open Folder.</p><p>Плюсы:</p><ul><li>Быстрое переключение между проектами через интерфейс или хоткеи;</li><li>Поддержка избранного и тэгов;</li><li>Можно автоматически подтягивать все папки из заданной директории.</li></ul><p><b>Чем полезен:</b> избавляет от рутинных действий при переходе между проектами — особенно когда важно не терять фокус и не сбиваться с рабочего темпа.</p><h2>9. Code Spell Checker</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker">Code Spell Checker </a>выявляет орфографические ошибки в комментариях, строках и именах переменных прямо в редакторе VSCode. Расширение подчёркивает опечатки волнистой линией и предлагает варианты исправления через Ctrl+. или Cmd+., что позволяет быстро исправить ошибки.</p><p>Поддерживает множество ЯП и словари для разных языков (английский, русский, немецкий — с дополнительными расширениями). Можно добавлять свои слова в пользовательский словарь или игнорировать определённые термины, чтобы адаптировать проверку под проект. Работает с camelCase и snake_case, не помечая их как ошибки.</p><p><b>Кому подойдёт</b>: разработчикам, которые пишут много комментариев или документации в коде, и тем, кто хочет избежать опечаток в строках, API или логах, чтобы повысить читаемость.</p><p>Плюсы:</p><ul><li>Мгновенное обнаружение ошибок с подсказками для исправления;</li><li>Гибкая настройка словарей и игнорируемых слов;</li><li>Поддержка технических терминов и различных стилей написания кода.</li></ul><p><b>Чем полезен:</b> экономит время на поиск и исправление опечаток, особенно в документации или пользовательских сообщениях, которые могут повлиять на восприятие проекта.</p><h2>10. REST Client</h2><p><a href="https://marketplace.visualstudio.com/items?itemName=humao.rest-client">REST Client </a>позволяет отправлять HTTP-запросы и просматривать ответы непосредственно в VSCode. Достаточно создать файл с расширением .http или .rest, написать запрос в простом текстовом формате, и вы увидите кнопку Send Request для моментального выполнения.</p><p>Поддерживает все типы запросов (GET, POST, PUT, DELETE и другие), авторизацию (Basic, OAuth, JWT), переменные окружения и даже генерацию кода на разных языках. Запросы можно сохранять в репозиторий, что удобно для командной работы. Расширение также позволяет использовать динамические переменные по типу {{$timestamp}} или {{$guid}}, всё для гибкой настройки запросов.</p><p><b>Кому подойдёт</b>: тем, кто работает с REST API и хочет тестировать эндпоинты прямо в VSCode, сохранять запросы в проекте и почти не переключаться между инструментами.</p><p>Плюсы:</p><ul><li>Простая отправка запросов через .http или .rest файлы с кнопкой Send Request;</li><li>Поддержка переменных окружения и авторизации для сложных API;</li><li>Возможность сохранять запросы в репозитории для совместной работы.</li></ul><p><b>Чем полезен:</b> ускоряет тестирование API, устраняет необходимость в сторонних приложениях. Запросы хранятся рядом с кодом, что упрощает документирование и повторное использование, особенно в проектах, где API-вызовы нужно часто проверять или делиться ими с командой.</p><p><i>А какими расширениями пользуетесь вы? Пишите в комментариях!</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Энтузиаст поднял веб-сервер… на одноразовом вейпе</title>
      <link>https://tproger.ru/news/entuziast-podnyal-veb-server--na-odnorazovoj-vejpe</link>
      <comments>https://tproger.ru/news/entuziast-podnyal-veb-server--na-odnorazovoj-vejpe?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/entuziast-podnyal-veb-server--na-odnorazovoj-vejpe</guid>
      <description><![CDATA[<p>Энтузиаст запустил веб-сервер на одноразовом вейпе: ARM Cortex-M0+ с 3 КБ ОЗУ поднял TCP/IP, HTTP и JSON-ответы без Wi-Fi и Ethernet</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/entuziast-podnyal-veb-server--na-odnorazovoj-vejpe">Энтузиаст поднял веб-сервер… на одноразовом вейпе</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Wi-Fi]]></category>
      <category><![CDATA[ARM]]></category>
      <category><![CDATA[TCP/IP]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 16 Sep 2025 04:52:09 GMT</pubDate>
      <content:encoded><![CDATA[<p>Энтузиаст под ником BogdanTheGeek <a href="https://bogdanthegeek.github.io/blog/projects/vapeserver/">запустил</a> полноценный HTTP-сервер на микроконтроллере из одноразового вейпа. Причем сервер умеет обслуживать страницы, обрабатывать запросы и даже возвращает JSON-ответы.</p><p>Все это — на чипе с 3 КБ оперативной и 24 КБ флеш-памяти, без сетевого интерфейса, Wi-Fi или Ethernet.</p><h2>Откуда там вообще чип?</h2><p>Автор проекта рассказывает, что давно коллекционировал одноразовые вейпы, но со временем устройства начали попадаться с куда более интересной начинкой.</p><p>Вместо обычного черного эпоксидного «блоба» он обнаружил маркированный микроконтроллер PUYA C642F15, позже идентифицированный как PY32F002B с ARM Cortex-M0+ частотой 24 МГц.</p><p>Вейпы, по сути, оказались носителями полностью программируемых микроконтроллеров.</p><h2>Как он вывел это в сеть?</h2><p>Wi-Fi, Ethernet или даже Bluetooth в таких устройствах, разумеется, нет. Но автор воспользовался семихостингом — механизмом отладки, при котором отладчик может отправлять и принимать данные через подключенный JTAG/SWD интерфейс.</p><p>Далее он применил довольно хакерское решение: использовал pyOCD для создания telnet-порта с выходом семихостинга, затем через socat пробросил это в виртуальное TTY-устройство, поверх него поднял SLIP (Serial Line Internet Protocol). Да, тот самый, из эпохи dial-up.</p><p>После этого на Linux можно было поднять IP-интерфейс sl0 и назначить IP-адрес — теперь микроконтроллер был виден как узел локальной сети.</p><h2>А веб-сервер?</h2><p>В качестве TCP/IP-стека был выбран uIP — крайне легкий вариант от Contiki OS, специально рассчитанный на 8- и 16-битные микроконтроллеры.</p><p>Он же включает в себя минимальный HTTP-сервер. После небольшой адаптации под семихостинг и исправления проблем с выравниванием памяти на ARM все заработало.</p><p>Причем не просто «заработало», а заработало удивительно быстро: после оптимизации буферов и избавления от побайтной передачи, RTT упали до 20 мс, а загрузка страницы — до 160 мс.</p><p>Несмотря на сверхограниченные ресурсы (всего 3 КБ RAM, из которых почти половина уже занята сервером), автор сумел:</p><ul><li>организовать буферизацию и кольцевой буфер;</li><li>запустить TCP/IP-стек и веб-сервер;</li><li>реализовать подсчет посещений и возврат JSON-ответов;</li><li>полностью уместить все в оставшиеся ~20КБ флеша.</li></ul><p>Он даже добавил простую статистику: уникальный ID микроконтроллера и количество посещений со времени последнего «краша».</p><h2>Зачем это все?</h2><p>Как отмечает сам автор, цель была не в создании полезного веб-сервера, а в демонстрации возможностей предельно скромного «железа», которое в обычной жизни просто выбрасывается.</p><p>Иными словами — это искусство, хакерская эстетика и демонстрация инженерной изобретательности.</p>]]></content:encoded>
    </item>
    <item>
      <title>2 млрд загрузок под ударом: в npm нашли вредоносные версии chalk, debug и еще 16 пакетов</title>
      <link>https://tproger.ru/news/--2-mlrd-zagruzok-pod-udarom--v-npm-nawli-vredonosnye-versii-chalk--debug-i-eshhe-16-paketov</link>
      <comments>https://tproger.ru/news/--2-mlrd-zagruzok-pod-udarom--v-npm-nawli-vredonosnye-versii-chalk--debug-i-eshhe-16-paketov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--2-mlrd-zagruzok-pod-udarom--v-npm-nawli-vredonosnye-versii-chalk--debug-i-eshhe-16-paketov</guid>
      <description><![CDATA[<p>В npm обнаружили вредоносные версии chalk, debug и ещё 16 пакетов: заражение коснулось проектов с 2 млрд загрузок, цель атаки — кража криптовалют</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--2-mlrd-zagruzok-pod-udarom--v-npm-nawli-vredonosnye-versii-chalk--debug-i-eshhe-16-paketov">2 млрд загрузок под ударом: в npm нашли вредоносные версии chalk, debug и еще 16 пакетов</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Ethereum]]></category>
      <category><![CDATA[Взлом]]></category>
      <category><![CDATA[Уязвимость]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 09 Sep 2025 04:29:19 GMT</pubDate>
      <content:encoded><![CDATA[<p>Компания Aikido Security <a href="https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised">выявила</a> масштабную атаку на экосистему npm. Злоумышленники опубликовали вредоносные версии 18 популярных JavaScript-библиотек, включая chalk, debug, supports-color, ansi-styles, strip-ansi и другие.</p><p>Эти пакеты используются в миллионах проектов по всему миру — общее количество загрузок только за неделю превышает 2 млрд.</p><h2>Как работал вредоносный код</h2><p>Внедренный JavaScript-код маскировался внутри библиотек и выполнялся исключительно в браузерной среде. Основная цель — перехват криптовалютных транзакций через подмену данных:</p><ul><li>внедрение в методы fetch, XMLHttpRequest, а также Web3-объекты (window.ethereum и др.);</li><li>отслеживание и подмена адресов кошельков при вызовах вроде sendTransaction, approve, transfer;</li><li>использование «похожих» адресов для подмены без визуального отличия;</li><li>манипуляции с параметрами транзакций на этапе подписи;</li><li>сохранение внешней «нормальности» интерфейса, чтобы пользователь ничего не заметил.</li></ul><p>Таким образом, атака была нацелена на незаметное хищение криптоактивов прямо из интерфейсов Web3-приложений.</p><h2>Как злоумышленники получили доступ</h2><p>Один из мейнтейнеров, обладающий доступом к части библиотек, стал жертвой фишинговой атаки.</p><p>Он получил письмо с поддельного адреса support@npmjs.help, зарегистрированного 5 сентября 2025 года. После компрометации аккаунта были опубликованы вредоносные версии пакетов.</p><p>Автор позднее подтвердил взлом и удалил некоторые версии, но часть вредоносных сборок осталась доступной, включая simple-swizzle.</p><h2>Список затронутых пакетов</h2><p>Наиболее популярные зараженные библиотеки:</p><ul><li>chalk</li><li>debug</li><li>supports-color</li><li>strip-ansi</li><li>ansi-regex</li><li>wrap-ansi</li><li>color-name</li><li>color-convert</li><li>color-string</li><li>chalk-template</li><li>has-flag</li><li>has-ansi</li><li>slice-ansi</li><li>error-ex</li><li>simple-swizzle</li><li>backslash</li><li>и другие</li></ul><p>Большинство из них используются транзитивно — через другие зависимости.</p><h2>Что делать разработчикам</h2><ol><li>Проверить package-lock.json или yarn.lock на наличие зараженных версий.</li><li>Использовать npm audit, snyk, Safe Chain или аналогичные инструменты для анализа зависимостей.</li><li>Обновить или зафиксировать безопасные версии пакетов.</li><li>Если проект работает с криптовалютой, проверить, не было ли компрометации в процессе использования.</li></ol><h2>Почему это важно</h2><p>Эта атака стала одной из крупнейших в истории JavaScript-экосистемы и подчеркивает уязвимость цепочек поставки (supply chain) в open-source.</p><p>Даже небольшая уязвимость или фишинговая атака на одного мейнтейнера может поставить под угрозу миллионы пользователей по всему миру.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как мы сделали SDK, CLI и MCP с ИИ за неделю</title>
      <link>https://tproger.ru/articles/kak-my-sdelali-sdk--cli-i-mcp-za-nedelyu-s-ii</link>
      <comments>https://tproger.ru/articles/kak-my-sdelali-sdk--cli-i-mcp-za-nedelyu-s-ii?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Сергей Пронин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-my-sdelali-sdk--cli-i-mcp-za-nedelyu-s-ii</guid>
      <description><![CDATA[<p>За 7 дней мы создали SDK, CLI и MCP-сервер для нашего API на Python. Рассказываем, как использовали Flask, Marshmallow и современные AI-инструменты, чтобы ускорить разработку и избежать типичных ошибок.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-my-sdelali-sdk--cli-i-mcp-za-nedelyu-s-ii">Как мы сделали SDK, CLI и MCP с ИИ за неделю</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 06 Sep 2025 12:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>За последнюю неделю мы в <a href="https://pingera.ru?utm_source=tproger">Pingera</a> активно работали над расширением возможностей нашего API, создав SDK, CLI и даже Model Context Protocol (MCP) сервер. В этой статье я расскажу, как мы это делали, и с какими трудностями столкнулись.</p><h2>Начнем с API</h2><p>Исторически сложилось так, что наш бэкенд написан на Python с использованием <a href="https://flask.palletsprojects.com/en/stable/">Flask</a>. На старте мы сразу заложили строгую типизацию данных с помощью библиотеки <a href="https://marshmallow.readthedocs.io/en/latest/">Marshmallow</a>. Это позволяет описывать схемы данных, которые API должен отдавать, и валидировать входящие запросы. Если вы работали с Django REST, то Marshmallow — это аналог их Schemas.</p><p>В какой-то момент мы решили, что хотим отдавать OpenAPI-спецификацию для нашего API. Мы попробовали <a href="https://github.com/jmcarp/flask-apispec">flask-apispec</a>, но столкнулись с проблемой: сгенерированная спецификация имела кучу ошибок в валидаторе, потому что библиотека миксовала OpenAPI v2 и v3. Частично это удалось решить с помощью ручных @doc() декораторов, но это было неудобно и больше походило на костыль, чем на нормальное решение.</p><p>В итоге я перешел на <a href="https://pypi.org/project/flask-smorest/">flask-smorest</a>. Эта библиотека активно развивается, работает поверх Flask и Marshmallow и умеет генерировать корректный OpenAPI v3. Все завелось «из коробки» с минимальными изменениями. Marshmallow-схемы поддерживают метаданные, которые smorest потом проксирует в спецификацию. Например, так выглядит поле type для проверок в нашем коде:</p><p>smorest берёт эти данные и добавляет их в спеку. Кроме того, можно (и нужно) описать, какие коды ответов возвращает ваш API-метод и другие детали:</p><p>Как только мы это сделали, то <a href="https://api.pingera.ru/swagger-ui">получили</a> приличный Swagger и валидную OpenAPI JSON-спецификаци</p><figure><img src="https://media.tproger.ru/user-uploads/117408/2025-08-29/36c0ac6a-1d91-490b-993a-82f239d82d81.png" alt="pingera swagger ui at api.pingera.ru/swagger-ui" /><figcaption>Swagger UI сгенерированный по openapi схеме</figcaption></figure><h2>SDK</h2><p>Далее мы поняли, что одного API недостаточно. Чтобы вызывать методы API из будущих CLI или MCP, нам не хотелось писать клиент с нуля. Поэтому мы решили создать SDK.</p><p>Если у вас есть OpenAPI-спецификация, то писать клиент с нуля бессмысленно. Для этого есть отличный инструмент — <a href="https://github.com/OpenAPITools/openapi-generator">openapi-generator</a>. Он позволяет сгенерировать API-клиенты для разных языков одной командой, просто получив на вход спецификацию.</p><p>Так мы получили полноценный Python-клиент и залили его в PyPI. Весь код можно найти на GitHub: <a href="https://github.com/pingera/pingera-python-sdk">pingera/pingera-python-sdk</a>. Особенно интересен файл <a href="https://github.com/pingera/pingera-python-sdk/blob/main/generate_client.py">generate-client.py</a>, который запускает openapi-generator и подменяет директории. После этого оставалось только залить все в <a href="https://pypi.org/project/pingera-sdk/">PyPI</a>, чтобы можно было использовать пакет в других сервисах и продуктах.</p><p>Конечно, имена сгенерированных методов не самые изящные, например v1_checks_check_id_results_check_result_id_get, но это в сто раз лучше, чем каждый раз писать requests.get, добавлять try-except обертки и волноваться об изменениях в API.</p><h2>Model Context Protocol (MCP) сервер</h2><p><a href="https://www.anthropic.com/news/model-context-protocol">MCP</a> — это стандарт, разработанный компанией Anthropic (создатели Claude). Его идея — стандартизировать взаимодействие LLM с данными или сервисами, превращая их в ИИ-агентов. Теперь не нужно объяснять агенту, как взять данные из базы данных или JIRA, достаточно указать MCP-сервер, который уже это умеет, и агент сам разберётся.</p><p>Мы в Pingera давно задумывались о том, как искусственный интеллект повлияет на мониторинг, поэтому создание собственного MCP-сервера было логичным шагом.</p><p>Для создания MCP-сервера мы использовали фреймворк <a href="https://github.com/jlowin/fastmcp">fastmcp</a>. Код MCP-инструментов («tools») повторяет структуру нашего SDK. Например, в MCP есть код, который выводит список проверок:</p><p>list_checks здесь просто вызывает метод SDK v1_checks_get().</p><p>Из-за такой повторяемости 80-85% кода MCP-сервера было сгенерировано ИИ-агентом Replit (можно взять любой другой). Весь код мы также опубликовали на GitHub: <a href="https://github.com/pingera/pingera-mcp">pingera/pingera-mcp</a>. Подробнее о сценариях использования можно прочитать в нашем блоге: <a href="https://pingera.ru/tpost/koti38ky91-pingera-mcp-server-blizhe-k-ai">Pingera MCP сервер — ближе к AI</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/117408/2025-08-29/80953ad1-3500-45f6-86fe-bec689125989.png" alt="MCP inspector для Pingera-mcp" /><figcaption>Интерфейс MCP inspector</figcaption></figure><h2>Command Line Interface (CLI)</h2><p>CLI — это про консоль и инженеров. Иногда гораздо удобнее выполнить команду в терминале, чем заходить в веб-интерфейс и кликать мышкой.</p><p>Создание CLI было похоже на работу над MCP — много похожих вызовов методов. ИИ-агент снова отлично справился, хотя были проблемы с выводом результатов. Мы взяли две библиотеки: <a href="https://typer.tiangolo.com/">Typer</a> для создания интерфейса и <a href="https://github.com/Textualize/rich">Rich</a> для красивого, цветного вывода в консоль. С Rich были трудности — то таблица уезжала, то цвет не тот. Но ИИ справился и помог решить эти проблемы.</p><figure><img src="https://media.tproger.ru/user-uploads/117408/2025-08-29/7d8ff6f3-944e-4f4f-b5a0-c174b1e7ef41.png" alt="Вывод pngr - pingera CLI" /><figcaption>Вывод pngr - CLI тузлы Pingera</figcaption></figure><p>Весь код также доступен на GitHub: <a href="https://github.com/pingera/pingera-cli">pingera/pingera-cli</a>. Из-за ограниченного контекста LLM, нельзя было скормить агенту всю спецификацию сразу. Поэтому пока наш CLI умеет работать только с проверками.</p><h2>Заключение</h2><p>Использование ИИ-агентов для генерации повторяющегося кода — это не просто удобная фича. Как показал наш опыт с Replit, такие помощники берут на себя рутину, освобождая время для более сложных и творческих задач. Когда 80-85% кода MCP-сервера было сгенерировано автоматически, это позволило нам сосредоточиться на архитектуре, логике и оптимизации, вместо того чтобы вручную переписывать однотипные вызовы методов.</p><p>В конечном итоге, ИИ-агенты — это не замена инженеру, а инструмент, который многократно может повысить производительность. В будущем мы видим всё больше сценариев, где ИИ будет не просто предлагать фрагменты кода, а создавать целые модули и сервисы на основе высокоуровневых спецификаций. Это приближает нас к тому моменту, когда разработка будет похожа на управление оркестром, где дирижер (разработчик или продакт) задает направление, а умные агенты исполняют отдельные партии, обеспечивая гармонию и эффективность всего процесса.</p>]]></content:encoded>
    </item>
    <item>
      <title>Релиз Grml 2025.08: отказ от Debian 10, чистка устаревших пакетов и улучшения в grml-zshrc</title>
      <link>https://tproger.ru/news/--reliz-grml-2025-08--otkaz-ot-debian-10--chistka-ustarevwih-paketov-i-uluchweniya-v-grml-zshrc</link>
      <comments>https://tproger.ru/news/--reliz-grml-2025-08--otkaz-ot-debian-10--chistka-ustarevwih-paketov-i-uluchweniya-v-grml-zshrc?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--reliz-grml-2025-08--otkaz-ot-debian-10--chistka-ustarevwih-paketov-i-uluchweniya-v-grml-zshrc</guid>
      <description><![CDATA[<p>Grml 2025.08 вышел на базе Debian 13 с ядром 6.12.41: отказ от Debian 10 и старых скриптов, улучшения grml-zshrc и обновление пакетов</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--reliz-grml-2025-08--otkaz-ot-debian-10--chistka-ustarevwih-paketov-i-uluchweniya-v-grml-zshrc">Релиз Grml 2025.08: отказ от Debian 10, чистка устаревших пакетов и улучшения в grml-zshrc</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 18 Aug 2025 06:05:42 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вышел новый <a href="https://grml.org/changelogs/README-grml-2025.08/">релиз</a> специализированного live-дистрибутива <b>Grml 2025.08</b> (кодовое имя <i>Oneinonein</i>), ориентированного на системных администраторов. Он основан на <b>Debian 13 «trixie»</b> и поставляется с ядром Linux 6.12.41.</p><p>В релизе задействован новый ключ подписи для репозиториев deb.grml.org, а сборочная система <b>grml-live</b> теперь включает пакеты, которые ранее поставлялись отдельно (grml-autoconfig, grml-etc, grml-scripts, grml-udev-config).</p><p>Также исправлены проблемы с опциями загрузки lang= и keyboard=, добавлена поддержка венгерской раскладки и немецкого варианта «neo». Сборки ISO теперь воспроизводимы.</p><h2>Поддержка и отказ от старого</h2><p>В инструменте <b>grml-debootstrap</b> прекращена поддержка Debian 10 «buster». Добавлена возможность пропускать udevadm на неподдерживаемых интерфейсах и улучшена работа с ключами и Device Tree.</p><p>Ряд устаревших скриптов (например, grml-config-root, grml-setservices, noprompt, noeject) удален. Другие перенесены в основной репозиторий grml-live.</p><h2>Улучшения в grml-zshrc</h2><p>В конфигурации zsh произошло несколько важных изменений:</p><ul><li>увеличен размер scrollback до 10 000 строк;</li><li>добавлена функция cdt для быстрого перехода во временные директории;</li><li>экспортируются все известные переменные LC_*;</li><li>убраны устаревшие функции (j, iso2utf, utf2iso и др.);</li><li>удален «checkhome hack», актуальный только для live-ISO.</li></ul><p>Все эти нововведения делают рабочую среду более легкой и удобной для кастомизации.</p><h2>Обновления пакетов</h2><p>Grml 2025.08 синхронизирован с пакетами Debian 13 на август 2025 года. В дистрибутив добавлены initramfs-tools-bin и tio, а пакеты clamav, clamav-freshclam, lockfile-progs и logrotate удалены.</p><p>Также изменена классификация clamav, вынесенная в отдельный класс FRESHCLAM.</p><p>Из дополнительного можно отметить:</p><ul><li>в grml2usb новая опция --format заменила --fat16 и теперь использует FAT32;</li><li>grml-hwinfo собирает информацию о ZFS в формате JSON;</li><li>в tmux.conf увеличена максимальная длина правого статус-блока.</li></ul><h2>Доступность</h2><p>Grml 2025.08 уже доступен для <a href="https://grml.org/download/">загрузки</a> на официальном сайте проекта. Следующий стабильный релиз намечен на конец 2025 года.</p>]]></content:encoded>
    </item>
    <item>
      <title>Релиз Go 1.25: умный GOMAXPROCS для контейнеров, ускоренный на 40% GC и «черный ящик» для отладки</title>
      <link>https://tproger.ru/news/reliz-go-1-25--umnyj-gomaxprocs-dlya-kontejnerov--uskorennyj-na-40--gc-i--chernyj-yashhik--dlya-otladki</link>
      <comments>https://tproger.ru/news/reliz-go-1-25--umnyj-gomaxprocs-dlya-kontejnerov--uskorennyj-na-40--gc-i--chernyj-yashhik--dlya-otladki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/reliz-go-1-25--umnyj-gomaxprocs-dlya-kontejnerov--uskorennyj-na-40--gc-i--chernyj-yashhik--dlya-otladki</guid>
      <description><![CDATA[<p>Go 1.25 получил container-aware GOMAXPROCS, GC быстрее на 40%, «черный ящик» Flight Recorder и новые инструменты для отладки и тестирования</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/reliz-go-1-25--umnyj-gomaxprocs-dlya-kontejnerov--uskorennyj-na-40--gc-i--chernyj-yashhik--dlya-otladki">Релиз Go 1.25: умный GOMAXPROCS для контейнеров, ускоренный на 40% GC и «черный ящик» для отладки</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[macOS]]></category>
      <category><![CDATA[ARM]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 13 Aug 2025 07:52:43 GMT</pubDate>
      <content:encoded><![CDATA[<p>Состоялся <a href="https://go.dev/doc/go1.25">релиз</a> Go 1.25. Разработчики заметно улучшили рантайм и инструменты, при этом не изменяя сам язык.</p><p>Одно из главных новшеств — <b>container-aware GOMAXPROCS</b>: теперь по умолчанию значение GOMAXPROCS учитывает лимиты CPU в cgroup.</p><p>На Linux это позволит процессам внутри контейнеров (например, в Kubernetes) автоматически адаптировать использование процессорных ресурсов к выделенной квоте.</p><p>Параметр обновляется динамически, если лимиты меняются, и может быть отключен через переменные окружения.</p><h2>Новый сборщик мусора — до 40% быстрее</h2><p>В экспериментальном режиме появился <b>garbage collector нового поколения</b> с улучшенной локальностью и масштабируемостью.</p><p>Он особенно эффективен в приложениях с большим количеством мелких объектов, снижая накладные расходы на GC на 10–40%. Включается через GOEXPERIMENT=greenteagc.</p><h2>Trace Flight Recorder: отладка по горячим следам</h2><p>Введен <b>runtime/trace.FlightRecorder</b> — «черный ящик» для приложений на Go.</p><p>Он постоянно пишет трейс в кольцевой буфер, позволяя при наступлении события выгрузить последние секунды исполнения в файл.</p><p>Нововведение значительно упрощает отладку редких и трудно воспроизводимых багов.</p><h2>Другие изменения в рантайме и инструментах</h2><ul><li>Исправлена ошибка компилятора с отложенной проверкой nil, из-за которой некорректный код мог выполняться без паники.</li><li>Добавлена поддержка DWARF5 — меньше отладочной информации и быстрее линковка.</li><li>Улучшено выделение памяти для слайсов — быстрее в ряде сценариев.</li><li>В Linux теперь можно видеть имена анонимных VMA ([anon: Go: heap]) в отладочных инструментах ядра.</li><li>Новый пакет testing/synctest для тестирования конкурентного кода с виртуальным временем.</li><li>Экспериментальный пакет encoding/json/v2 с ускоренным парсингом и расширенной конфигурацией маршалера.</li><li>Новый метод WaitGroup.Go для удобного запуска горутин с учетом синхронизации.</li></ul><h2>Платформенные изменения</h2><p>Go 1.25 требует macOS 12 и выше. 32-битная Windows/ARM-платформа будет удалена в следующем релизе.</p><p>На RISC-V появился режим сборки плагинов и поддержка профиля RVA23U64.</p>]]></content:encoded>
    </item>
    <item>
      <title>Вышел Python 3.14 RC1: релиз-кандидат с ускоренным интерпретатором. Финальный релиз — в октябре</title>
      <link>https://tproger.ru/news/--vywel-python-3-14-rc1--reliz-kandidat-s-uskorennym-interpretatorom--finalnyj-reliz---v-oktyabre</link>
      <comments>https://tproger.ru/news/--vywel-python-3-14-rc1--reliz-kandidat-s-uskorennym-interpretatorom--finalnyj-reliz---v-oktyabre?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--vywel-python-3-14-rc1--reliz-kandidat-s-uskorennym-interpretatorom--finalnyj-reliz---v-oktyabre</guid>
      <description><![CDATA[<p>Вышел Python 3.14 RC1 с JIT-компилятором и free-threaded режимом — релиз уже доступен, финальная версия выйдет в октябре</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--vywel-python-3-14-rc1--reliz-kandidat-s-uskorennym-interpretatorom--finalnyj-reliz---v-oktyabre">Вышел Python 3.14 RC1: релиз-кандидат с ускоренным интерпретатором. Финальный релиз — в октябре</a>»</p>]]></description>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[macOS]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 23 Jul 2025 07:32:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Команда Python <a href="https://pythoninsider.blogspot.com/2025/07/python-314-release-candidate-1-is-go.html">объявила</a> о выходе первой версии релиз-кандидата Python 3.14.</p><p>Это финальная стадия перед полноценным релизом, который запланирован на <b>7 октября 2025 года</b>. Версия 3.14.0rc1 уже <a href="https://www.python.org/downloads/release/python-3140rc1/">доступна</a> для загрузки на официальном сайте Python.</p><h2>Что важно знать</h2><p>Python 3.14 RC1 — это максимально приближенная к финалу сборка. Отныне в код ядра будут вноситься <b>только багфиксы</b>, прошедшие ревью. Следующий и последний релиз-кандидат — <b>3.14.0rc2</b> — выйдет 26 августа.</p><p>Важно: с этого момента <b>не будет изменений ABI</b>, а бинарные сборки, созданные под RC1, будут совместимы с финальной версией 3.14.</p><p>Разработчиков библиотек призывают начать адаптацию своих пакетов под 3.14 и публиковать .whl-сборки на PyPI.</p><h2>Главное в Python 3.14</h2><p>Список нововведений в 3.14 впечатляет — это не просто минорный апдейт. Вот ключевые фичи:</p><h2>Улучшения производительности и инфраструктуры</h2><ul><li><b>Экспериментальный JIT-компилятор</b> включён в официальные сборки для macOS и Windows.</li><li><b>Новый тип интерпретатора</b>, обеспечивающий ускорение кода (для некоторых компиляторов).</li><li><b>PEP 779: Free-threaded Python</b> — полная поддержка свободных потоков.</li><li><b>Улучшенные сообщения об ошибках</b>.</li><li><b>Оптимизация</b> генерации UUID v3–v5 (ускорение до 40%).</li></ul><h2>Новые языковые возможности и модули</h2><ul><li><b>PEP 750:</b> шаблонные строки t"..." — аналог f-строк, но для кастомной обработки.</li><li><b>PEP 649:</b> отложенное вычисление аннотаций типов.</li><li><b>PEP 765:</b> теперь return, break, continue нельзя использовать так, чтобы они покидали finally.</li><li><b>PEP 734:</b> изоляция интерпретаторов в stdlib.</li><li><b>Новый модуль</b> compression.zstd для поддержки алгоритма Zstandard.</li><li><b>Цветной вывод</b> в CLI-инструментах (unittest, argparse, json, calendar).</li><li><b>Обновление</b> uuid, pdb, поддержка подключения к удалённым процессам.</li></ul><h2>Разработка и отладка</h2><ul><li><b>PEP 768:</b> интерфейс внешней отладки без накладных расходов.</li><li><b>Новый CLI</b> для анализа запущенных Python-процессов.</li><li><b>HMAC теперь реализован внутри Python</b> с формально верифицированной библиотекой HACL*.</li></ul><h2>Что убрали или изменили</h2><ul><li>Подписи PGP для артефактов релиза больше не предоставляются. Вместо них — поддержка <b>Sigstore</b>.</li><li>Установщик для Windows заменяется новым <b>Python Install Manager</b> (доступен в Microsoft Store).</li></ul><p>Внимание: несмотря на стабильность RC1, использовать его в продакшене пока не рекомендуется. Но для тестов и подготовки библиотек — самое время.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что еще есть в терминале Linux: 7 команд, которые экономят кучу времени</title>
      <link>https://tproger.ru/articles/chto-eshhe-est-v-terminale-linux--7-komand--kotorye-ekonomyat-kuchu-vremeni</link>
      <comments>https://tproger.ru/articles/chto-eshhe-est-v-terminale-linux--7-komand--kotorye-ekonomyat-kuchu-vremeni?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/chto-eshhe-est-v-terminale-linux--7-komand--kotorye-ekonomyat-kuchu-vremeni</guid>
      <description><![CDATA[<p>Семь советов для ускорения работы в терминале Linux. Как быстро обработать файлы, отладить Bash-скрипт и редактировать длинные пути в Линукс.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/chto-eshhe-est-v-terminale-linux--7-komand--kotorye-ekonomyat-kuchu-vremeni">Что еще есть в терминале Linux: 7 команд, которые экономят кучу времени</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[Регулярные выражения]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 22 Jul 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p><b>Сколько статей про «полезные команды Linux» вы уже прочитали?</b> Алиасы, history, базовые горячие клавиши — факты для джунов, которые опытным админам уже снятся. Если свободно пользуетесь grep и awk, создаете циклы, применяете регулярные выражения — эта статья для вас.</p><p>Рассказываем про 7 команд, влияющие на скорость работы в терминале. Вы узнаете:</p><ul><li>про встроенные bash-операции, которые заменяют пайплайны,</li><li>про способы работы с файловыми дескрипторами,</li><li>про wildcards, которые избавляют от сложных конструкций с find.</li></ul><p>Каждая команда в подборке решает конкретную проблему:</p><ul><li>массовая обработка файлов,</li><li>отладка скриптов,</li><li>работа с длинными путями.</li></ul><h2>1. Bash variable expansions</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/b5f2b475-b18e-49cb-a00c-9197ab87b9f4.jpg" alt="" /></figure><p>Вместо <i>basename</i>, <i>cut </i>для простых операций со строками можно использовать встроенные возможности Bash:</p><p><b>%</b> режет справа до первого совпадения, <b>%%</b> — до последнего. Символ <b>#</b> работает слева направо.</p><p>В реальной работе это помогает при массовой обработке файлов. Например, есть 1000 логов, и нужно каждый переименовать.</p><p>Если использовать <i>basename</i>, запустится 1000 отдельных процессов. <b>Variable expansions</b> работают без <i>fork/exec</i>, без задержек на создание процессов.</p><p>Bash variable expansions используют в циклах с файлами и при работе с массивами. Когда скрипт обрабатывает сотни файлов, разница в скорости становится заметной. Еще и код выглядит чище.</p><h2>2. Here-string (&lt;&lt;&lt;)</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/ef72e05d-6f80-49aa-842b-32c209d8b724.jpg" alt="" /></figure><p>Here-string упрощает передачу строковых данных в команды без создания временных файлов или использования echo с пайпом.</p><p>Реальная экономия времени проявляется при отладке и модификации скриптов. Например, когда SQL-запрос или конфигурация зашиты в <i>here-документ</i>. С <b>here-string </b>данные собраны в одном месте, легко редактируются и переиспользуются.</p><p>Работает не только с базами данных. Отправка в API, конфигурирование сетевых устройств через expect, передача команд в Docker — через &lt;&lt;&lt; код будет понятнее, а сопровождение проще.</p><h2>3. /proc/$$/fd</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/f314de69-9459-4ecb-ab62-2c7b5c7b54f3.jpg" alt="" /></figure><p>Каждый процесс имеет стандартные дескрипторы <b>0</b> (stdin), <b>1</b> (stdout), <b>2</b> (stderr), которые представлены как символические ссылки. Директория <b>/proc/$$/fd </b>предоставляет доступ к файловым дескрипторам текущего процесса:</p><p>Переменная <b>$$</b> содержит PID текущего процесса, поэтому /proc/$$/fd ведет к дескрипторам именно вашего шелла.</p><p>Практическое применение — отладка перенаправлений и работа с дескрипторами в сложных скриптах:</p><h2>4. Wildcards с диапазонами</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/08dbc2a0-5e2e-49a3-9486-a042a0480369.jpg" alt="" /></figure><p>Про <b>*</b> и <b>?</b> говорят чаще, чем про диапазоны в квадратных скобках. Такие маски используют реже, а зря — они решают массу задач по отбору файлов.</p><p>Wildcards автоматически раскрываются шеллом в список подходящих файлов — это их основная функция. Кавычки нужны только когда передаете символы [, ] как литеральные:</p><p>Экономия времени заметна при работе с логами, бэкапами и в скриптах автоматизации. Например, для архивации файлов с определенными номерами, очистки временных файлов с нужными паттернами.</p><h2>5. sudo !!</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/9434c945-925f-40b3-9062-994fce2091c6.jpg" alt="" /></figure><p>Набрали длинную команду, нажали Enter, получили «<b>Permission denied</b>». Рука на рефлексе тянется к стрелке вверх и Home, чтобы добавить sudo в начало.</p><p>Вот способ в разы быстрее:</p><p>Двойное восклицание <b>!!</b> — это ссылка на предыдущую команду целиком. Bash подставит всю строку со всеми аргументами и ключами. Кажется мелочью, но для админа, который 10 раз в день забывает sudo, это серьезная оптимизация.</p><p>Двойное восклицание универсально и работает не только с sudo:</p><ul><li><b>time !!</b> для замера времени выполнения,</li><li><b>nohup !! &amp;</b> для запуска в фоне,</li><li><b>strace !!</b> для отладки.</li></ul><p>Если между командой и sudo !! выполнялись другие команды, восклицание сработает для последней из них. Для поиска конкретной команды в истории используйте <b>!строка</b>.</p><h2>6. ^старое^новое</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/1888b625-8ae5-4d0e-a73d-25260ff7d893.jpg" alt="" /></figure><p>Основной способ исправления опечатки — стрелка вверх, поиск ошибки, исправление. Смотрите, как можно сделать это побыстрее:</p><p>Символ <b>^</b> ищет первое вхождение слова и заменяет его. Работает с последней командой.</p><p>Заменяется только первое вхождение. Если ошибочное слово встречается несколько раз, способ не сработает. В таких случаях придется использовать классическое редактирование или <i>history expansion</i>.</p><h2>7. Alt+.</h2><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-07-10/4d1c03bb-66b2-4cb0-8c3e-ce80c3406ec5.jpg" alt="" /></figure><p>Создали файл с длинным именем — теперь его нужно отредактировать, переместить, изменить права. Каждый раз перепечатывать путь утомительно и чревато ошибками.</p><p>Комбинация <b>Alt+.</b> (Alt + точка) вставляет последний аргумент предыдущей команды в текущую позицию курсора. Повторное нажатие перебирает аргументы из более ранних команд.</p><p>Экономия времени проявляется при работе с файлами и директориями. Например: распаковали архив, теперь нужно зайти в созданную папку, затем посмотреть содержимое, потом изменить права. Вместо того, чтобы 3 раза печатать один путь, можно 3 раза нажать Alt+.</p><p>Еще этой комбинацией вставляются:</p><ul><li>имена пользователей,</li><li>IP-адреса,</li><li>названия сервисов,</li><li>параметры конфигурации.</li></ul><p>Alt + точка работает в большинстве шеллов. Привыкнув к ней, начинаешь использовать на автомате.</p><h2>Что запомнить</h2><ul><li><b>${filename%.*} </b>и встроенные операции со строками работают быстрее внешних утилит. % режет справа, # — слева. Полезно при работе с циклами для массовой обработки файлов.</li><li><b>&lt;&lt;&lt;</b> — here-string удобен для передачи коротких строковых данных в команды. С многострочными данными лучше использовать here-document или переменные.</li><li><b>/proc/$$/fd</b> — доступ к файловым дескрипторам текущего процесса. Ускоряет отладку перенаправлений и работу с дескрипторами.</li><li><b>file[1-5] и [^b]* </b>— диапазоны в wildcards для точного отбора файлов. Кавычки нужны только для передачи литеральных символов.</li><li><b>sudo !!</b> — повторяет последнюю команду с sudo. Также работает с time !!, nohup !! &amp;. Если между нужной командой и !! выполнялись другие операции, используйте !строка для поиска конкретной команды в истории.</li><li><b>^старое^новое</b> — заменяет первое вхождение в предыдущей команде. Работает только с последней командой и заменяет первое совпадение.</li><li><b>Alt+. </b>— вставляет последний аргумент предыдущей команды. Повторное нажатие перебирает аргументы из истории команд. Экономит время при работе с длинными путями.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>ИИ без регистрации и VPN: быстрый доступ к GPT, Claude и Gemini</title>
      <link>https://tproger.ru/articles/ii-bez-registracii-i-vpn--bystryj-dostup-k-gpt--claude-i-gemini</link>
      <comments>https://tproger.ru/articles/ii-bez-registracii-i-vpn--bystryj-dostup-k-gpt--claude-i-gemini?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/ii-bez-registracii-i-vpn--bystryj-dostup-k-gpt--claude-i-gemini</guid>
      <description><![CDATA[<p>Как пользоваться GPT, Claude и Gemini в России без VPN и регистрации: подборка сервисов для быстрого старта.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/ii-bez-registracii-i-vpn--bystryj-dostup-k-gpt--claude-i-gemini">ИИ без регистрации и VPN: быстрый доступ к GPT, Claude и Gemini</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[SEO]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Статистика]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[VPN]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Яндекс]]></category>
      <category><![CDATA[Анализ данных]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[5g]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Notion]]></category>
      <category><![CDATA[Промпты]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 17 Jul 2025 17:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>В 2025 году пользоваться GPT-4, Claude и Gemini — всё ещё сложная задача для пользователей из России, они работают через VPN, требуют подтверждения номера телефона или наличия зарубежной банковской карты. Но есть решения, которые работают без этих ограничений — они используют API-прокси, локальные модели или специальные инструменты для обхода.</p><p>В этом обзоре собрали самые выгодные сервисы, которые дают доступ к языковым моделям без VPN и дополнительной регистрации либо предлагают альтернативные варианты. Платформы подходят для учебы, бизнеса и творчества, а их функционал проверен на реальных кейсах.</p><h2>1. Deeplom Ru — генератор академических работ на базе ИИ</h2><p>Сервис<a href="https://t.me/DeeplomAIBot"> Deeplom Bot </a>создан специально для студентов и школьников, которым нужно быстро подготовить реферат, курсовую или дипломную работу. В отличие от других ИИ-ассистентов, он фокусируется именно на академических задачах, используя адаптированные версии GPT и Claude. Это позволяет генерировать тексты, соответствующие требованиям ГОСТ и вузовских стандартов. Сервис работает в условиях сжатых сроков или при работе над несколькими проектами одновременно.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/f6b8d3f2-d9b2-4e9a-9ea5-4a3848eec653.png" alt="" /></figure><h3>Как это работает</h3><p>Работать с сервисом можно двумя способами: <a href="https://deeplom.ru">через сайт</a> или телеграм-бота (@DeeplomAIBot). Достаточно ввести тему работы и через несколько минут система сгенерирует демо-версию. Она включает оглавление, введение, первую главу и список литературы.</p><p>Особенности:</p><ul><li>полностью анонимная работа — для<br />демо-версии не нужен даже email;</li><li>сервис позволяет бесплатно<br />ознакомится с демо-версией перед покупкой работы;</li><li>автоматическое оформление по<br />ГОСТу, включая заголовки, списки литературы;</li><li>конструктор оглавления для ручной<br />корректировки структуры работы;</li><li>поддержка формата DOCX для<br />удобного редактирования.</li></ul><p>Сервис использует специально дообученные модели ИИ, которые учитывают требования российских вузов. Например, система автоматически корректирует стилистику под академические стандарты.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/a61bb6e5-a0d0-4b30-8714-6ffc0655e589.png" alt="" /></figure><h3>Тарифы</h3><p>Бесплатная версия дает доступ только к части работы. Полный текст можно приобрести за 499-1299 рублей в зависимости от сложности и объема текста. Для постоянных пользователей есть система скидок.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/6e77de0d-3328-4ce7-b015-fca1ea89096a.png" alt="" /></figure><h2>2. Wikibot — интеллектуальный помощник для бизнеса</h2><p><a href="https://wikibot.pro/">Wikibot</a> — профессиональное решение для отделов продаж и служб поддержки, оно автоматизирует до 70% рутинных обращений клиентов. Сервис подходит для компаний, которые ежедневно обрабатывают сотни однотипных вопросов через разные каналы коммуникации.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/d348e349-fa00-4dd7-a584-e3bdbe72d7ab.png" alt="" /></figure><h3>Как работает система</h3><p>После регистрации через Google или Telegram пользователь получает доступ к панели управления, где может загрузить базу знаний в различных форматах — PDF, DOCX или подключить облачные хранилища вроде Notion и Confluence. Фишка Wikibot в том, что он не только отвечает по шаблонам, а анализирует контекст каждого обращения, используя мощные языковые модели GPT-4.1 и DeepSeek V3,и умеет выполнять действия в системах, как это делает человек. И всё это вы можете настроить самостоятельно в ЛК.</p><p>Особенности сервиса:</p><ul><li>автоматическое создание сделок в<br />amoCRM и других CRM-системах через API;</li><li>автоматическая классификация<br />тикетов по категориям через теги или группы;</li><li>возможность обучать бота на<br />истории переписок с клиентами;</li><li>функция «Анализ ответа» —<br />показывает, на какие разделы базы знаний опирался ИИ;</li><li>гибкая система кредитов (1 кредит<br />— примерно 10 ответов на тарифе Мини);</li><li>интеграция с популярными<br />платформами: HelpDeskEddy, Битрикс24, Юздеск,<br />Jivo;</li><li>возможность размещения бота во<br />внутреннем контуре (по запросу).</li><li>чат-бота можно добавить на свой сайт или интегрировать в собственный телеграм-бот</li></ul><p>Сервис использует специальный алгоритм ранжирования ответов, который учитывает релевантность информации и историю взаимодействий с конкретным клиентом. Система способна автоматически определять эмоциональный тон обращения и адаптировать стиль ответа — от формального до дружелюбного.</p><h3>Тарифные планы</h3><p>Wikibot предлагает гибкую систему тарифов:</p><ul><li>Мини (1000 руб./мес) — подходит<br />для тестирования и небольших проектов.</li><li>Старт (4900 руб./мес) — оптимален<br />для малого бизнеса.</li><li>Рост (19900 руб./мес) — решение<br />для компаний с устоявшимися бизнес-процессами и сформированной командой<br />поддержки.</li><li>Бизнес (49900 руб./мес) —<br />комплексное решение для крупных компаний.</li></ul><p>Каждый тариф включает определённое количество кредитов, а дополнительные запросы можно докупать по мере необходимости. Для новых пользователей доступен тестовый период со 100 бесплатными кредитами.</p><h2>3. Acetone AI — профессиональный инструмент для обработки изображений</h2><p><a href="https://acetone.ai/">Acetone AI</a> создан для быстрой обработки изображений — дизайнеров, маркетологов, владельцев интернет-магазинов и контент-менеджеров. Сервис используют при подготовке большого количества товарных карточек для маркетплейсов или создании рекламных материалов.</p><h3>Как работает платформа</h3><p>Главное преимущество Acetone AI — моментальный старт. Пользователь может загрузить изображение на главной странице и получить результат через несколько секунд. После регистрации через email или VK предоставляется 50 бесплатных кредитов (эквивалент 5 операций по удалению фона ежедневно).</p><p>Особенности сервиса:</p><ul><li>уникальные алгоритмы обработки<br />изображений, работающие независимо от зарубежных ИИ;</li><li>пакетная обработка до 50<br />изображений одновременно;</li><li>встроенный редактор для тонкой<br />настройки результатов удаления фона и объектов;</li><li>многослойный редактор создания<br />карточек товара, баннеров и прочих изображений;</li><li>генератор фона с помощью<br />нейросети;</li><li>API для интеграции с<br />корпоративными системами и автоматизации процессов;</li><li>поддержка популярных форматов:<br />JPG, PNG, WEBP.</li></ul><p>Acetone AI использует специально обученные нейросетевые модели, которые сохраняют детализацию сложных элементов (например, волос или полупрозрачных материалов). Сервис корректно обрабатывает тени и рефлексы, что важно для профессиональных фотографов и ретушеров.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/cd5e15d7-1ac6-4539-a332-898d6369e0e9.png" alt="" /></figure><h3>Тарифная политика</h3><p>Сервис предлагает такие тарифные планы:</p><ul><li>Бесплатный тариф: 10 обработок,<br />доступ к базовым функциям редактора, ограничение на размер загружаемых файлов<br />(до 5 МБ).</li><li>Подписка «Стандарт» (от 989<br />руб./мес): неограниченное количество операций, приоритетная обработка,<br />увеличенный лимит на размер файлов (до 25 МБ), доступ к премиум-фильтрам.</li><li>Корпоративные решения:<br />индивидуальные квоты на обработку, персональный менеджер, API с увеличенными<br />лимитами.</li></ul><p>По данным сервиса, среднее время обработки одного изображения составляет менее 3 секунд, а точность распознавания объектов достигает 98%.</p><h2>4. F5 AI — универсальная платформа для работы с ИИ</h2><p><a href="https://f5ai.ru/">F5 AI</a> — это многофункциональное решение с доступом к разным языковым моделям, включая GPT и Claude. Платформа ориентируется на широкий круг пользователей: от маркетологов, создающих SEO-контент, до разработчиков, автоматизирующих написание кода. Особенность сервиса — адаптация под специфические бизнес-задачи российских компаний.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/a91a28e2-5c26-4ff3-9f1b-72b7de8cccd2.png" alt="" /></figure><h3>Как работает</h3><p>Пользователи получают доступ через веб-интерфейс или API, при этом не нужно использовать VPN. Зачисление 500 бонусных рублей происходит при входе через Яндекс или почту.</p><p>Особенности сервиса:</p><ul><li>поддержка разных языковых моделей<br />(GPT-4, Claude и других) с возможностью выбора оптимального варианта для<br />конкретной задачи;</li><li>создание корпоративных<br />телеграм-ботов с индивидуальной базой знаний;</li><li>гибкая система управления доступом<br />для командной работы;</li><li>API для интеграции с внутренними<br />бизнес-процессами компании;</li><li>личная база знаний для хранения<br />корпоративных и рутинных документов – в общем вся информация, которую вы хотите скормить ИИ;</li><li>бот, который обращается к<br />документам из вашей базы знаний, чтобы давать более точные ответы на вопросы,<br />основываясь на имеющихся данных.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-18/01bd0324-6030-4ec0-b769-df719641c33a.png" alt="" /></figure><p><b>Дополнительно: </b></p><ol><li><b>Meet AI</b> — анализ<br />     видеовстреч и транскрипции созвонов из Контур.Толк. Внутри есть кастомные<br />     шаблоны для анализа разных типов встреч.</li><li><b>Работа с ИИ Ассистентами </b>—<br />     создание своих умных ассистентов на основе моделей внутри с гибкой<br />     системой настройки параметров. Новых ассистентов можно также обучить на<br />     собственных файлах для внутренних процессов компании.</li></ol><h3>Тарифы</h3><p>Сервис работает без абонентской платы и подписок: вы платите только за использованные токены. Цена зависит от модели и объема работы. Для пополнения баланса используйте СБП, банковскую карту или счет юрлица — в последнем случае получите закрывающие документы. Все транзакции детализированы по моделям, а расходы доступны в аналитике по дням и месяцам.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-18/647caecf-4d3e-464f-a76b-04658c3e170a.png" alt="" /></figure><p><b>Запоминаем главное про платформу: </b>работает без VPN в России, объединяет всех вендоров в одном интерфейсе, предлагает русскоязычную и англоязычную поддержку и интерфейс. Корпоративным клиентам доступны индивидуальные тарифы с увеличенными лимитами API и приоритетной поддержкой.</p><h2>5. Jadve AI</h2><p><a href="https://jadve.com/ru?utm_source=tproger&amp;utm_medium=article&amp;utm_campaign=promo">Jadve AI </a>подходит для специалистов, ежедневно работающих с разными форматами контента. Маркетологи получают инструмент для быстрого создания промо-материалов, программисты — помощника для работы с кодом, а дизайнеры — возможность оперативно визуализировать идеи. Сервис объединяет три ключевых направления: текстовую генерацию, создание изображений и производство видео.</p><h3>Как устроена работа платформы</h3><p>Доступ к возможностям <a href="https://jadve.com/ru?utm_source=tproger&amp;utm_medium=article&amp;utm_campaign=promo">Jadve AI</a> предоставляется через веб-интерфейс или тг-бота. Бесплатные функции включают неограниченное использование GPT-4o mini и 10 ежедневных запросов к GPT-4o. Для работы с графикой и видео нужно оформить подписку.</p><p>Особенности:</p><ul><li>комплексный доступ к 10+<br />актуальным ИИ-моделям разных категорий;</li><li>гибкая система оплаты токенами без<br />привязки к конкретным моделям;</li><li>программа лояльности с<br />персональными скидками для активных пользователей;</li><li>полная синхронизация между<br />веб-версией и ботом.</li></ul><h3>Практическое применение</h3><p>Программисты могут генерировать и оптимизировать код блоками до 1000 строк. Маркетологи — создавать  рекламные изображения для соцсетей и короткие промо-ролики по готовым шаблонам. Дизайнеры — использовать  инструмент для быстрого концепт-арта и адаптации графики под целевые платформы.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/e69f0199-4b01-43d8-8304-e4e4e754b8fd.png" alt="" /></figure><h3>Условия использования</h3><p>Бесплатный режим даёт доступ к базовым текстовым функциям. Платные тарифы начинаются от 499 рублей в месяц: 100 тыс. токенов в день, которые можно расходовать на любые операции — генерацию текста, изображений или видео. Дополнительные пакеты токенов можно докупить.</p><h2>6. TEXTAGRAM — умный генератор описаний товаров</h2><p><a href="https://textagram.ru/?utm_source=tproger">TEXTAGRAM</a> разработан специально для продавцов на маркетплейсах (Wildberries, Ozon, Яндекс.Маркет) и владельцев интернет-магазинов. Сервис помогает автоматизировать самый сложный этап — создание качественных описаний товаров. По данным сайта, использование TEXTAGRAM сокращает время на заполнение карточек товаров в 5-7 раз.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/c80cc132-298e-4b3f-bae2-4933497eef02.png" alt="" /></figure><h3>Как работает платформа</h3><p>Пользователь загружает фотографию товара, ИИ генерирует несколько вариантов описаний с учётом ключевых характеристик. Например, для загруженного изображения кроссовок система может создать текст:</p><p><i>«Эти отличные кроссовки с амортизирующей подошвой сочетают в себе стиль и комфорт, идеально подходят для прогулок, и для легких ежедневных тренировок. Выполнены из высококачественных материалов, обеспечивающих долговечность и удобство.</i></p><p><i>Ключевые характеристики:</i></p><ul><li><i>Амортизирующая подошва для максимального комфорта</i>;</li><li><i>Дышащие материалы, обеспечивающие вентиляцию</i>;</li><li><i>Стильный дизайн, подходящий к любому образ</i>у».</li></ul><p>Особенности сервиса:</p><ul><li>автоматическое определение<br />ключевых характеристик товара по изображению;</li><li>генерация SEO-оптимизированных<br />описаний с релевантными ключевыми словами; поддержка различных стилей текста<br />— продающий, технический, минималистичный;</li><li>возможность массового экспорта в<br />CSV-формат для загрузки на маркетплейсы;</li><li>адаптированные языковые модели для<br />точного описания товаров российского рынка.</li></ul><p>TEXTAGRAM использует специально обученную версию ChatGPT, которая учитывает специфику российских маркетплейсов. Сервис умеет определять не только общие категории товаров, но и конкретные материалы — например, отличает экокожу от натуральной.</p><p>В ближайших планах разработчиков — добавить функции автоматического подбора тегов и категорий для Wildberries и Ozon, чтобы ещё больше упростить процесс заполнения карточек товаров.</p><h3>Текущие условия использования</h3><p>На момент публикации (июль 2025 года) сервис находится в стадии MVP и доступен пользователям в бесплатном режиме.</p><h2>7. TryChatGPT — универсальный доступ к GPT-моделям с гибкой системой запросов</h2><p>Сервис <a href="https://trychatgpt.ru/?utm_medium=organic&amp;utm_campaign=tproger">TryChatGPT</a> даёт доступ к GPT-моделям для широкого круга задач: от генерации текстов до создания изображений. Основная аудитория — пользователи, которым нужен простой и понятный доступ к ИИ без сложных систем токенов. Подходит для копирайтеров, разработчиков и маркетологов.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/65c0c77b-b580-4cf0-924f-45c75b396b11.png" alt="" /></figure><h3>Как это работает</h3><p>Система построена на модели запросов вместо токенов. Пользователь может начать работу сразу после посещения сайта — для тестирования доступно 200 бесплатных запросов к GPT-4.1 nano каждые 30 дней. При этом регистрация не обязательна, но этом случае в функционале будете видеть рекламу.</p><p>Особенности:</p><ul><li>прозрачная система запросов вместо<br />сложных токенных расчётов;</li><li>функция памяти для сохранения<br />контекста между чатами;</li><li>настраиваемый системный промт;</li><li>отсутствие обязательной подписки с<br />автопродлением.</li></ul><h3>Основные возможности</h3><p>Сервис помогает в переводе текстов, генерации кода и обработке изображений. Отличительная черта — возможность обучения ИИ: при команде «Запомни» система сохраняет ключевые данные из запроса и использует их в последующих диалогах. Для работы с файлами и расширенными функциями нужна регистрация через Яндекс, VK или email.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/712a9abd-db67-411c-81a1-bf2362dfa951.png" alt="" /></figure><h3>Тарифная политика</h3><p>Бесплатный тариф включает 200 запросов к базовой модели. Платные варианты:</p><ul><li>Без рекламы (590 руб./мес): 500<br />запросов к базовым моделям + обработка изображений.</li><li>Творческий (990 руб./мес): акцент<br />на генерацию изображений (50 запросов).</li><li>Премиум (1990 руб./мес):<br />расширенные лимиты для всех категорий моделей.</li></ul><p>Сервис не работает в Индонезии, Индии и Китае, но полностью доступен в России без VPN. По сравнению с аналогами, TryChatGPT предлагает более предсказуемую систему оплаты — пользователи точно знают, сколько запросов получают за свои деньги.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/86cb4b46-d543-4182-9c4d-9aaa96889093.png" alt="" /></figure><h2>8. Обзор платформы PR‑CY для работы с языковыми моделями</h2><p><a href="https://pr-cy.ru/chat-gpt/">PR‑CY </a>— инструмент для работы с поисковыми данными и генеративными моделями. Через веб‑интерфейс доступны версии ChatGPT (5‑Mini, 4o‑Mini, GPT‑5, O3, O4‑Mini), Claude (4 Sonnet, 3.5 Haiku), Gemini 2.5 Flash и DeepSeek v3. Бесплатный доступ сильно ограничен: всего 5-10 запросов в день. Для серьезной работы потребуется платная подписка. Для проведения комплексных исследований и построения собственных ассистентов доступны платные модели (GPT‑5, GPT‑4o, Gemini 2.5 Pro, DeepSeek R1 и др.), но их использование оплачивается отдельно.</p><p><b>По сути, платформа рассчитана на:</b></p><ul><li>разработчиков и продуктовых команд, которым нужно быстро проверить гипотезы на разных версиях языковых моделей;</li><li>SEO‑специалистов — интегрированные MCP‑серверы помогают вытаскивать вордстат, SERP‑выдачу, контент страниц и другие метрики;</li><li>тех, кто хочет собрать собственного AI‑ассистента из готовых компонентов, используя свои базы знаний.</li></ul><h3>Как это работает</h3><p>Платформа работает через веб-интерфейс, доступна без VPN. После входа пользователь выбирает модель и отправляет ей запрос. Для настройки поведения предусмотрены параметры температуры, длины контекста и системного промпта. Есть возможность загрузить файлы (PDF, DOC и др.) или изображения для анализа, подключить RAG‑базы знаний для ответов из собственных данных, а также настроить голосовой ввод и озвучку ответа.</p><h3>Сценарии использования</h3><ul><li><b>Гибкие настройки генерации:</b> температура, длина контекста и системный промпт позволяют тонко регулировать ответы ботов под конкретный сценарий.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-09-12/388e0c01-972f-4d10-a3f6-cb8c94ed73f2.png" alt="" /></figure><ul><li><b>Загрузка и анализ файлов:</b> сервис умеет работать с документами и изображениями (PDF, DOC и др.), что удобно для анализа технической документации или баз знаний.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-09-12/a6e60eea-e87c-405c-aaab-4048547b0fd4.png" alt="" /></figure><ul><li><b>RAG‑базы знаний:</b> можно подключить собственные источники данных, чтобы модель отвечала, опираясь на внутреннюю информацию команды.</li><li><b>MCP‑интеграция:</b> для SEO‑задач доступна подключаемая сервисом SEO‑MCP от PR‑CY.</li><li><b>Голосовой ввод и голосовой ответ: </b>ускоряет взаимодействие — можно проговаривать вопросы и слушать ответы.</li></ul><h3>Лимиты и тарифы</h3><p>Базовый доступ даёт 10 запросов в день (5 — без регистрации). Для интенсивной работы предусмотрены тарифы с большим числом запросов и отдельные платные модели; пробный период на тарифе Профи — 7 дней, после чего требуется оплата. Стоимость продвинутых моделей оплачивается отдельно. На повышенном тарифе вы можете протестировать более мощные модели или создать собственных ассистентов.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2025-09-12/12fef01b-44d4-4083-962b-a8587c34438e.png" alt="" /></figure><h3>Кому подойдет</h3><p>Платформа ориентирована на разработчиков, работающих с различными языковыми моделями, и SEO-специалистов, а также специалистам по продукту и контенту — благодаря встроенному SEO‑инструментарию и ИИ‑редактору. Возможность создавать ассистентов и подключать RAG‑базы — функционал позволяет создавать ассистентов, однако это требует технических навыков и дополнительных затрат, что минус для обычного пользователя. Недостатки: лимиты на бесплатном тарифе, необходимость доплачивать за лучшие модели, сложность интерфейса.</p><h2>9. BotHub — универсальный агрегатор ИИ-моделей</h2><p><a href="https://bothub.chat/">BotHub</a> многофункциональная платформа с доступом к ИИ-моделям, включая ChatGPT-4 Turbo, Midjourney v6 и Claude 3. Сервис популярен среди маркетологов и разработчиков, которым нужно единое решение для работы с разными типами ИИ без переключения между площадками.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/96180a7f-3d64-4968-90be-54a7f91d369a.png" alt="" /></figure><h3>Как это работает</h3><p>После регистрации на сайте пользователь получает доступ к единому интерфейсу, где может одновременно работать с текстовыми и графическими ИИ. Внутри есть функция транскрибации аудио через Whisper, которая распознает до 95% русской речи.</p><p>Особенности:</p><ul><li>комбинированный доступ к текстовым<br />и графическим ИИ в одном интерфейсе;</li><li>анализ документов с возможностью<br />извлечения ключевых данных; высокоточная транскрибация<br />аудиофайлов;</li><li>API для интеграции с<br />корпоративными CRM-системами.</li></ul><p>По данным площадки, BotHub обрабатывает более 50 000 запросов ежедневно, при этом среднее время отклика не больше двух секунд. Новые юзеры могут использовать пробный период, платные тарифы начинаются от 200 руб. в месяц за 10 000 токенов. Система поддерживает оплату через СБП и российские банковские карты.</p><h2>10. Fabula AI — мультифункциональный инструмент для работы с контентом</h2><p><a href="https://fabula-ai.com/">Fabula AI</a> предлагает комплексное решение для работы с визуальным контентом. Сервис больше ориентируется на владельцев интернет-магазинов, маркетологов и контент-менеджеров, которым нужна быстрая обработка изображений и генерация текстовых описаний.</p><h3>Как это работает</h3><p>Доступ к сервису происходит через веб-интерфейс и телеграм-бота (@fabula_ai_bot). Основные возможности включают улучшение качества фотографий с апскейлингом до 4K, автоматическое удаление фона с поддержкой пакетной обработки и генерацию описаний товаров на основе их изображений. Есть функция преобразования обычных фото в аниме-стиль.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/bd1a2db7-122d-447d-8b30-5232ef6ffb1e.png" alt="" /></figure><p>Особенности и преимущества:</p><ul><li>профессиональное улучшение<br />качества изображений;</li><li>пакетная обработка фотографий для<br />интернет-магазинов;</li><li>встроенный ChatGPT 3.5 для<br />создания текстового контента;</li><li>уникальные стилистические фильтры<br />для изображений.</li></ul><h3>Тарифы</h3><p>Сервис обрабатывает до 1000 изображений ежечасно, при этом сохраняя стабильную скорость работы. Доступна недельная подписка за 299 рублей с безлимитным количеством обработок или месячный абонемент за 999 рублей.</p><h2>11. Chad AI — универсальный ИИ-ассистент в Telegram</h2><p><a href="https://chadgpt.ru/">Chad AI</a> — многофункциональный телеграм-бот, который объединяет доступ к восьми популярным нейросетевым моделям, включая GPT-4o, Claude 3 и Midjourney V6. Особенно востребован программистами, маркетологами и студентами.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/ebf81d73-0b86-42d4-8e3c-d246352051be.png" alt="" /></figure><h3>Как это работает</h3><p>После запуска бота @chadgpt_bot пользователь получает мгновенный доступ ко всем функциям. Сервис предлагает комплексное решение для работы с текстовыми моделями, включая генерацию кода и аналитику данных, а также инструменты для создания изображений. Есть готовые промпты — шаблоны запросов, которые помогают эффективно взаимодействовать с нейросетями даже новичкам.</p><p>Особенности:</p><ul><li>полная локализация на русский язык<br />с адаптированными ответами;</li><li>поддержка голосового ввода и<br />веб-поиска актуальной информации;</li><li>сохранение истории диалогов в<br />течение 30 дней.</li></ul><h3>Тарифы и условия</h3><p>Бесплатная версия позволяет совершать до семи запросов в день с использованием модели GPT-4o Mini. Премиум-подписка стоимостью от 590 рублей в месяц открывает неограниченный доступ к GPT-4, DALLE, Midjourney и другим моделям.</p><h2>12. GenAPI — универсальный шлюз для работы с ИИ-моделями</h2><p><a href="https://gen-api.ru/?utm_source=vc&amp;utm_medium=text">GenAPI</a> представляет собой удобный API-шлюз, предоставляющий доступ к популярным языковым моделям, включая GPT-4, Claude и Gemini. Сервис подходит разработчикам технологичных компаний, которым требуется надежная интеграция ИИ-возможностей в свои продукты без настройки сложной инфраструктуры.</p><h3>Как работает</h3><p>После регистрации пользователь получает персональный API-ключ, позволяющий отправлять запросы к различным ИИ-моделям. Сервис поддерживает как текстовые, так и мультимодальные запросы, включая обработку изображений и аудио. Доступна тонкая настройка параметров запросов для получения структурированных ответов в JSON-формате.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-07-17/aff829b2-df3b-41e3-ad82-76ab19701663.png" alt="" /></figure><p>Особенности:</p><ul><li>единый API для работы с различными<br />ИИ-моделями;</li><li>поддержка потоковой передачи<br />данных;</li><li>детальная статистика и мониторинг<br />использования.</li></ul><h3>Тарифы и условия</h3><p>GenAPI предлагает прозрачную систему тарифов с оплатой за фактическое использование. Бесплатный тариф позволяет полноценно протестировать сервис. Профессиональные тарифы начинаются от 2000 руб. в месяц и предоставляют увеличенные лимиты, а также приоритетную обработку запросов.</p><h2>Как выбрать подходящий сервис</h2><p>При выборе ИИ-помощника ориентируйтесь на три ключевых критерия:</p><ul><li>определите основной тип задач —<br />генерация текстов, обработка изображений или анализ данных;</li><li>проверьте требования к<br />конфиденциальности — некоторым пользователям критична анонимность, другим —<br />интеграция с рабочими инструментами;</li><li>оцените удобство интерфейса —<br />телеграм-боты подходят для быстрых запросов, веб-версии — для более сложной<br />работы.</li></ul><p>Обращайте внимание на наличие бесплатного тестового периода и гибкость тарифов. Российские сервисы без VPN часто выгоднее зарубежных аналогов, но могут уступать в функциональности. Для бизнеса важна поддержка API, студентам — оформление по ГОСТ. Начинайте с бесплатных версий, чтобы оценить качество ответов без финансовых рисков.</p>]]></content:encoded>
    </item>
    <item>
      <title>10 библиотек Python, которые меняют карьеру</title>
      <link>https://tproger.ru/articles/10-bibliotek-python--kotorye-menyayut-kareru</link>
      <comments>https://tproger.ru/articles/10-bibliotek-python--kotorye-menyayut-kareru?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/10-bibliotek-python--kotorye-menyayut-kareru</guid>
      <description><![CDATA[<p>10 библиотек Python, которые помогут прокачаться в аналитике, ML и разработке. Как они работают и почему меняют карьеру.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/10-bibliotek-python--kotorye-menyayut-kareru">10 библиотек Python, которые меняют карьеру</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Jupyter Notebook]]></category>
      <category><![CDATA[Визуализация]]></category>
      <category><![CDATA[Образование]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Анализ данных]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 17 Jul 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>У Python тысячи библиотек, но лишь немногие действительно меняют карьеру. Они помогают не просто решать задачи, а ускорять проекты, прокачивать навыки и выходить на следующий уровень в аналитике, машинном обучении и разработке. В этом материале мы собрали 10 библиотек, которые помогут зарабатывать на Python и развивать навыки.</p><h2>1. Pandas</h2><p>Pandas — библиотека для работы с данными в Python, позволяющая легко загружать, анализировать, очищать и преобразовывать числовую информацию в удобной табличной форме. По сути, это Excel, который смог, и позволяет делать всё автоматизировано и на порядки быстрее.</p><p>Библиотека строится вокруг двух ключевых структур: <b>Series</b> (одномерный массив с индексами); <b>DataFrame </b>(таблица с индексами и колонками).</p><h3>Какие задачи решает библиотека</h3><p>Pandas полезна для следующих задач:</p><ul><li>Сам анализ данных: можно быстро фильтровать, группировать, агрегировать и строить сводные таблицы.</li><li>Очистка данных: удаляем пустые строки, заменяем значения, приводим типы.</li><li>Загрузка данных из CSV, Excel, SQL.</li><li>Визуальная разведка данных (EDA) перед построением моделей.</li><li>Подготовка данных для ML и отчётов.</li><li>Автоматизация отчётов и ETL-пайплайнов.</li></ul><p>Благодаря Pandas аналитик превращается в инженера данных, а ML-специалист может сосредоточиться на моделях, а не на ручной подготовке датасетов.</p><h3>Как пользоваться</h3><p>Ниже разберём простейший кейс: нужно загрузить данные о зарплатах разработчиков из CSV, посчитать среднюю зарплату по языкам программирования и отобрать топ-5.</p><h3>Почему это меняет карьеру</h3><p>Работа с Pandas становится границей между знанием Python и умением решать задачи бизнеса. Для <b>джуна </b>это шанс сразу показать практическую пользу: выгрузки, отчёты и базовый анализ можно делать в десятки раз быстрее и аккуратнее, чем вручную в эксельке.</p><p>Для <b>аналитика</b> Pandas превращается в главный рабочий инструмент, позволяя не просто проверять гипотезы и делать сводные таблицы, а строить полноценные отчётные пайплайны, автоматизировать рутинные выгрузки и концентрироваться на сути данных, а не на правках ручками.</p><p>Для <b>ML-инженера</b> владеть Pandas — значит уметь готовить датасеты качественно; быстро очищать и приводить данные к нужному виду, что напрямую влияет на результат моделей. Без этого работа над проектами машинного обучения часто превращается в бесконечную возню с данными.</p><p>Наконец, даже для <b>разработчиков</b> Pandas может стать неожиданным бустом в карьере. Например, когда нужно автоматизировать отчёты для бизнеса или быстро анализировать логи и данные из БД без поднятия дашбордов — Pandas даёт гибкость и скорость, которые редко даёт что-то ещё в экосистеме Python.</p><h2>2. Django</h2><p>Django — фреймворк для веб-разработки на Python, который позволяет быстро создавать надежные и масштабируемые веб-приложения. Он следует принципам DRY (Don’t Repeat Yourself — не повторяй себя), предоставляя разработчику ORM, роутинг, систему авторизации, админку, работу с формами, шаблонами и инструментами безопасности из коробки.</p><p>Django подходит как стартапам, которым нужно быстро выйти на рынок, так и крупным проектам с миллионами пользователей. Это не просто библиотека, а полноценный каркас для построения и сопровождения веб-сервисов.</p><h3>Какие задачи решает библиотека</h3><p>Каркас, действительно, каркасный. Задачи следующие:</p><ul><li>Создание веб-приложений и API любой сложности.</li><li>Быстрая разработка MVP, прототипов и коммерческих проектов.</li><li>Упрощение работы с базами данных через ORM, без написания сырого SQL.</li><li>Построение административных панелей для управления данными без ручной разработки.</li><li>Гибкая маршрутизация и работа с формами, валидацией и шаблонами.</li><li>Реализация аутентификации, авторизации и защиты приложений.</li></ul><p>Django позволяет сосредоточиться на бизнес-логике и продукте, не тратить недели на настройку инфраструктуры.</p><h3>Как пользоваться</h3><p>Устанавливаем:</p><p>Создаем проект и приложение:</p><p>Пример модели:</p><p>Миграция базы данных:</p><p>Создание админки:</p><p>После этого можно запустить сервер:</p><p>И перейти по адресу http://127.0.0.1:8000/admin для управления записями через готовую админ-панель.</p><h2>3. PyTorch</h2><p>PyTorch — мощная библиотека Python. Она позволяет строить и обучать нейронные сети, проводить вычисления с автоматическим дифференцированием и работать с GPU для ускорения самих вычислений.</p><p>Главное отличие PyTorch от других ML-фреймворков — динамическая вычислительная графика (define-by-run): модель строится и изменяется во время выполнения кода, что даёт гибкость при создании и отладке сложных моделей.</p><p>Сегодня PyTorch используется в продакшен системах, научных исследованиях, компьютерном зрении, NLP и генеративных моделях, занимая ведущее место в индустрии.</p><h3>Какие задачи решает библиотека</h3><p>В функционал PyTorch входят:</p><ul><li>Построение нейронных сетей любой сложности (CNN, RNN, трансформеры);</li><li>Обучение и тестирование моделей на CPU и GPU;</li><li>Реализация кастомных слоёв и loss-функций;</li><li>Разработка и деплой ML/AI моделей в продакшен;</li><li>Быстрая итерация гипотез с удобной отладкой.</li></ul><p>С PyTorch можно начать с простых нейронных сетей, а затем перейти к реализации современных архитектур.</p><h3>Как пользоваться</h3><p>Установим PyTorch (на CPU, для GPU потребуется версия с CUDA):</p><p>Рассмотрим кейс обучения простой нейронной сети для классификации рукописных цифр MNIST.</p><p>После обучения можно использовать torch.save() для сохранения модели и torch.load() для загрузки в продакшн.</p><h3>Почему это меняет карьеру</h3><p>PyTorch — билет в мир современной разработки AI и машинного обучения. Владение инструментом даёт <b>разработчику</b> возможность уверенно войти в области, которые продолжают оставаться топовыми на рынке: искусственный интеллект, компьютерное зрение, NLP, генерация изображений и видео и т.д.</p><p>Для <b>начинающего ML/AI-специалиста </b>PyTorch помогает лучше понять, как устроены нейронные сети, и под капотом увидеть, как происходят вычисления. Это ускоряет рост навыков и делает разработчика востребованным в исследованиях и R&amp;D-проектах.</p><p>Для <b>дата-сайентистов</b> PyTorch позволяет превратить исследовательские ноутбуки в готовые к деплою модели, благодаря PyTorch Lightning, TorchScript и ONNX.</p><p>Для<b> разработчиков, которые хотят выйти на рынок AI</b>, PyTorch — это мастхев: проекты в стартапах и крупных компаниях всё чаще строятся вокруг него. Умение писать кастомные loss-функции, проектировать сложные пайплайны обучения, настраивать обучение на кластерах и GPU — компетенции, которые существенно бустят зарплату.</p><p>PyTorch в целом помогает расширять портфолио: с ним можно создавать генеративные модели, строить LLM, участвовать в соревнованиях и работать с самыми современными подходами в машинном обучении.</p><h2>4. Polars</h2><p>Polars — современная библиотека для обработки данных в Python, созданная как альтернатива Pandas. Она использует колоночную архитектуру и многопоточность, что позволяет работать с большими объёмами данных значительно быстрее и с меньшим потреблением памяти.</p><p>Polars вдохновлена Pandas, но её API оптимизировано для производительности и удобства, а также даёт разработчику возможность писать цепочки ленивых вычислений, которые оптимизируются перед выполнением. Это делает её отличным инструментом для аналитиков, дата-инженеров и дата-сайентистов, которым нужно обрабатывать данные быстро.</p><h3>Какие задачи решает библиотека</h3><p>Polars явно есть, чем гордиться:</p><ul><li>Загрузка, очистка и преобразование больших датасетов;</li><li>Анализ данных с использованием цепочек преобразований;</li><li>Быстрая агрегация и группировка данных;</li><li>Ленивые вычисления: построение пайплайнов преобразования данных, которые выполняются только при вызове collect().</li><li>Обработка данных, которые не помещаются в память, за счёт эффективности и колоночной архитектуры.</li></ul><p>Если Pandas начинает притормаживаться на данных в несколько гигабайт, Polars обычно продолжает работать быстро, позволяя без боли обрабатывать большие CSV.</p><h3>Как пользоваться</h3><p>Установка:</p><p>Давайте загрузим данные и проведем базовые трансформации:</p><p>А вот и пример ленивых вычислений:</p><p>В чем особенность:</p><ul><li>pl.read_csv загружает данные сразу.</li><li>pl.scan_csv создаёт план вычислений для последующей оптимизации.</li><li>Используются выражения (pl.col, .with_columns, .agg), которые композируются без создания промежуточных копий, это ускоряет процесс.</li></ul><h3>Почему это меняет карьеру</h3><p>Polars меняет карьеру, потому что даёт преимущество в скорости и эффективности при работе с данными. Там, где Pandas уже не справляется, полярный медведь приходит на помощь.</p><p>Для <b>дата-инженеров</b> Polars полезен при построении ETL и пайплайнов обработки данных, где важна скорость и предсказуемое потребление ресурсов. Его можно использовать в продакшен-скриптах, для подготовки данных к ML и для автоматизации отчётности.</p><p>Для <b>дата-сайентистов </b>Polars даёт возможность анализировать больше данных за меньшее время, быстро итерировать гипотезы и ускорять исследования. Его API достаточно близок к Pandas, поэтому переход не требует месяцев переучивания.</p><p>Освоение Polars показывает работодателям, что ты не просто знаешь стандартные инструменты, но умеешь выбирать оптимальные решения для реальных задач, повышая эффективность работы команды. В эпоху роста данных это критично для любого Python-разработчика, работающего с аналитикой и машинным обучением.</p><h2>5. FastAPI</h2><p>FastAPI — современный фреймворк для создания API на Python, заточенный под скорость, асинхронность и валидацию данных из коробки. Он построен на Starlette и Pydantic, автоматически создаёт OpenAPI-документацию, поддерживает асинхронное программирование и позволяет писать производительные REST и WebSocket API с минимальным количеством кода.</p><p>Вместо долгой настройки, как у Flask или Django, в FastAPI многое готово изначально: удобная работа с запросами и ответами, декларативная валидация, документация Swagger, асинхронность и высокая производительность без лишних усилий.</p><h3>Какие задачи решает</h3><p>Задач, действительно, много:</p><ul><li>Быстрая разработка REST API для мобильных и веб-приложений;</li><li>Создание бэкенда для ML/DS моделей (деплой моделей в виде API);</li><li>Построение микросервисов с хорошей производительностью;</li><li>Реализация websocket-серверов и асинхронных API;</li><li>Подготовка внутренних инструментов или бэкендов для MVP.</li></ul><p>FastAPI помогает быстро запускать API и уверенно масштабировать его в полевых условиях. Это один из немногих фреймворков Python, который по скорости работы сопоставим с Node.js и Go.</p><h3>Как пользоваться</h3><p>Во-первых, нужно установить FastAPI и Uvicorn (используем ASGI-сервер для запуска):</p><p>Простейший API-пример с эндпоинтом GET /:</p><p>Запускаем сам сервер:</p><p>После запуска API будет доступен по адресу http://127.0.0.1:8000/. Автоматически доступна интерактивная документация Swagger по адресу http://127.0.0.1:8000/docs.</p><p>FastAPI поддерживает валидацию параметров запроса, тел запросов и путей прямо через типы Python. Например, простой эндпоинт с параметром:</p><p>При вызове http://127.0.0.1:8000/items/10?q=test FastAPI автоматически проверит, что item_id — это число, и распарсит q как строку.</p><h3>Почему это меняет карьеру</h3><p>FastAPI — билет в мир бэкенда, где скорость и чистота кода имеют довольно высокое значение. Для <b>Python-разработчика </b>это возможность быстро освоить создание API и микросервисов, не увязнув в громоздкой настройке, как в Django, и при этом получить систему, готовую к продакшену.</p><p>Для <b>ML-специалиста</b> FastAPI становится инструментом для деплоя моделей: можно обернуть пайплайн предсказаний в API, подключить авторизацию или логирование и получить работающий сервис за считанные дни.</p><p>Вообще умение быстро поднимать и поддерживать API — навык, который ценят в бигтехе и стартапах. На разработчиков, которые владеют FastAPI, часто равняются: они умеют превращать идеи бизнеса в работающие сервисы за минимальное время.</p><h2>6. Typer</h2><p>Typer — современная библиотека для создания CLI-приложений на Python с минимальным количеством кода и автоматической генерацией документации. Автор библиотеки — Себастьян Рамирес, создатель FastAPI.</p><p>Главная особенность Typer — использование type hints для автоматического парсинга аргументов командной строки. Вы получаете удобную и читаемую CLI с поддержкой автодополнения и цветного вывода за считанные минуты.</p><h3>Какие задачи решает библиотека</h3><p>Список задач такой:</p><ul><li>Создание CLI-утилит любого уровня сложности.</li><li>Быстрое прототипирование и упаковка Python-скриптов в удобные инструменты для продакшена.</li><li>Генерация подробной справки (--help) и автодополнения команд.</li><li>Облегченная поддержка и масштабирование CLI за счёт структуры и читаемого кода.</li><li>Организация CLI с подкомандами, вложенными аргументами и обработкой ошибок.</li></ul><p>Typer использует аннотацию типов и минимум шаблонного кода.</p><h3>Как пользоваться</h3><p>Установка:</p><p>Пример минимальной CLI:</p><p>Теперь можно запустить из консоли:</p><p>Результат будет такой: Привет, Алиса! Тебе 25 лет.</p><h3>Почему это меняет карьеру</h3><p>Typer меняет карьеру тем, что открывает путь к созданию удобных CLI-инструментов, которые автоматизируют рутину и повышают продуктивность.</p><p>С Typer можно быстро превращать свои Python-скрипты в надежные утилиты, которыми удобно пользоваться и другим разработчикам, и сотрудникам из других отделов. CLI-приложения часто становятся клеем инфраструктуры: они позволяют автоматизировать деплой, миграции БД, сбор данных, интеграцию с внешними API и локальную разработку.</p><p>Если вы <b>Data Scientist или ML-инженер</b>, Typer позволяет оборачивать пайплайны в CLI, которые легко запускать из Jenkins, Airflow или вручную. Если вы <b>DevOps или Backend-инженер</b>, можете создавать CLI для работы с инфраструктурой и сервисами без сложных зависимостей.</p><p>Кроме того, работа с Typer улучшает навык структурирования кода, понимание CLI, использования type hints и разработки инструментов, которые делают работу проще для других. А это, очевидно, ценится в любой команде и повышает востребованность специалиста.</p><h2>7. Rich</h2><p>Rich — библиотека Python для красивого форматирования и интерактивного отображения информации в терминале. С её помощью можно выводить цветные таблицы, маркдаун, прогресс-бары, подсвеченный синтаксис кода, деревья каталогов и логирование в понятной и привлекательной форме.</p><p>Rich создана для того, чтобы «оживить» консоль Python, сделать логи удобными для восприятия, а CLI-инструменты — профессионально выглядящими без лишних усилий. Это библиотека, которая улучшает и UX, и DX.</p><h3>Какие задачи решает</h3><p>Про красоту не забываем! Задачи следующие:</p><ul><li>Цветное и структурированное логирование, понятное при чтении логов в реальном времени.</li><li>Отображение прогресс-баров для долгих операций.</li><li>Вывод таблиц, деревьев каталогов, JSON прямо в терминале.</li><li>Подсветка синтаксиса кода для CLI-инструментов.</li><li>Создание CLI-интерфейсов, которые выглядят профессионально и современно.</li><li>Улучшение читаемости при отладке скриптов.</li></ul><p>С помощью Rich можно быстро сделать понятными даже сложные данные при отладке или демонстрации.</p><h3>Как пользоваться</h3><p>Установка Rich:</p><p>Для примера выведем таблицу с подсветкой в консоли:</p><p>В результате в терминале получится цветная таблица, которая выглядит понятно и презентабельно.</p><h3>Почему это меняет карьеру</h3><p>Rich — это библиотека, которая помогает быстро повысить качество любого CLI-инструмента или дев-опыт в команде. <b>Разработчик</b>, который использует Rich, делает свои инструменты удобными не только для себя, но и для коллег: логирование становится понятным, а отладка скриптов — наглядной.</p><p>Во многих стартапах и продвинутых командах важна скорость обратной связи при тестировании пайплайнов и автоматизаций, и Rich помогает выводить ключевую информацию максимально читаемо.</p><p>Кроме того, Rich позволяет быстро создавать CLI-интерфейсы, которые выглядят как продакшен-продукты, даже если это внутренние инструменты. Руководство будет радоваться и думать о вас как о крутом разрабе.</p><p>Для <b>дата-инженеров и разработчиков DevOps</b> Rich полезна при создании админ-утилит и при мониторинге пайплайнов, для <b>Python-разработчиков</b> — при создании библиотек и фреймворков с CLI.</p><h2>8. LangChain</h2><p>LangChain — фреймворк для создания приложений на базе LLM, например, GPT, Claude, Mistral, Gemini. Он позволяет строить цепочки обработки запросов, интегрировать LLM с данными и инструментами, добавлять память и управление состояниями, а также связывать работу модели с внешними API и базами знаний.</p><p>LangChain предоставляет удобный слой абстракции над вызовами LLM и ускоряет разработку чат-ботов, RAG-приложений, агентов с инструментами, систем анализа документов и других AI-сервисов.</p><h3>Какие задачи решает</h3><p>Список внушительный:</p><ul><li>Интеграция LLM в Python-приложения без необходимости писать тот самый клеевой код вручную.</li><li>Построение цепочек с последовательной обработкой сообщений, включая преобразования и вызовы внешних функций.</li><li>Добавление памяти в чат-боты для сохранения истории общения и контекста.</li><li>Использование агентов для динамического вызова инструментов (веб-поиск, базы данных, API).</li><li>Создание RAG-систем с интеграцией LLM и векторных БД.</li><li>Быстрая сборка прототипов LLM-приложений, которые можно развернуть в продакшен.</li></ul><h3>Как пользоваться</h3><p>Установка:</p><p>Создадим простую цепочку с чатом GPT:</p><p>Благодаря единым абстракциям, можно гибко комбинировать цепочки, память и вызов внешних инструментов, не усложняя код.</p><h3>Почему это меняет карьеру</h3><p>LangChain меняет карьеру, потому что открывает новый пласт Python-разработки в AI и LLM-инженерии, быстро превращая пользователя GPT в создателя полноценных AI-приложений. Вместо того чтобы писать хаотичный клеевой код, вы начинаете системно проектировать цепочки запросов, учитесь строить продуманные промпты и объединять их с инструментами, памятью и внешними API.</p><p>Работа с LangChain погружает в практическую LLM-инженерию: вы начинаете создавать RAG-приложения, которые умеют искать и анализировать данные перед генерацией ответа и строить агентов. Это востребовано в продуктах, где нужно подключать ИИ к базам знаний, автоматизировать задачи и разрабатывать интерактивные системы, которые реально используют модели в продакшене.</p><p>LangChain позволяет быстро собирать и запускать MVP AI-продуктов, что дает конкурентное преимущество при создании стартапов или внутренних сервисов. А ещё учит мыслить структурами и проектировать масштабируемую архитектуру LLM-приложений и видеть, как генеративный ИИ можно превратить в рабочий инструмент.</p><h2>9. SQLAlchemy</h2><p>SQLAlchemy — это мощная ORM и toolkit для работы с базами данных в Python, позволяющая писать SQL-запросы декларативно, создавать модели таблиц и управлять транзакциями в Python-коде без ручного написания SQL.</p><p>Библиотека даёт разработчику два уровня контроля:</p><ul><li>Core: низкоуровневая работа с SQL выражениями и соединениями;</li><li>ORM: высокоуровневая декларативная работа с моделями, классами и связями между таблицами.</li></ul><p>SQLAlchemy поддерживает PostgreSQL, MySQL, SQLite, Oracle и другие СУБД, давая единую абстракцию, без привязки к конкретному движку.</p><h3>Какие задачи решает</h3><p>Пул задач следующий:</p><ul><li>Описание таблиц в виде Python-классов и управление ими через сессии;</li><li>Создание, чтение, обновление и удаление данных;</li><li>Миграция SQL на декларативный стиль без потери гибкости;</li><li>Полный контроль над транзакциями и выполнением запросов;</li><li>Работа с асинхронными приложениями при создании FastAPI/Django-приложений;</li><li>Экранирование параметров, которое снижает вероятность SQL-инъекций и ошибок.</li></ul><h3>Как пользоваться</h3><p>Создадим минимальный пример для SQLite с таблицей пользователей:</p><p>Этот код создаёт базу example.db, таблицу users, добавляет туда одного пользователя и выводит всех пользователей в базе. При необходимости можно использовать SQLAlchemy Core для написания гибких запросов вручную, если нужно работать ближе к SQL.</p><h3>Почему это меняет карьеру</h3><p>SQLAlchemy меняет карьеру <b>Python-разработчика</b> тем, что даёт понимание системной работы с данными, архитектуры приложений и взаимодействия с реальными базами данных. Вы учитесь строить продуманные бэкенды, которые работают с транзакциями, миграциями, связями между таблицами и сложными выборками.</p><p>Знание SQLAlchemy открывает дорогу в мир API, микросервисов и продуктов, где требуется качественное управление данными и гибкая логика работы с БД. Работа с SQL теперь совсем не страшная.</p><h2>10. Seaborn</h2><p>Seaborn — библиотека для визуализации данных на Python, построенная поверх Matplotlib и упрощающая создание информативных и стильных графиков с минимальным количеством кода.</p><p>Она автоматически заботится о красивых стилях, цветах, разметке графиков, легендах и позволяет легко строить распределения, линейные графики, тепловые карты и другие визуализации.</p><p>Библиотека тесно интегрируется с Pandas DataFrame, позволяя использовать колонки данных напрямую для построения графиков, что делает её идеальной для EDA (разведочного анализа данных) и подготовки визуализаций для отчётов и презентаций.</p><h2>Какие задачи решает</h2><p>Визуализация безумно важна, особенно в контексте дата-аналитики. Seaborn отвечает за:</p><ul><li>Быстрое построение информативных графиков для анализа данных и поиска инсайтов;</li><li>Автоматическую обработку ошибок отображения и масштабирования, что экономит время;</li><li>Поддержку сложных визуализаций по типу ящиков с усами или тепловых карт без десятков строк кода;</li><li>Стилизацию графиков без ручных настроек Matplotlib;</li><li>Возможность добавлять статистические элементы (линию регрессии, KDE, распределение);</li><li>Интеграцию с Jupyter Notebook для интерактивного анализа данных.</li></ul><h3>Как пользоваться</h3><p>Допустим, у нас есть датасет с данными о чаевых:</p><p>В три строки мы получаем чистый и читаемый ящик с усами, показывающий, как счет за ужин распределяется по дням недели.</p><p>Для построения более сложных графиков можно использовать диаграмму рассеяния:</p><p>Тут мы добавляем цветовую кодировку по полу, чтобы увидеть зависимости между переменными.</p><h3>Почему это меняет карьеру</h3><p>Seaborn меняет карьеру, потому что даёт навык визуального анализа данных, что критично в современной аналитике и дата-инженерии. Умение быстро строить графики и видеть аномалии, распределения и взаимосвязи между переменными превращает работу с данными из слепого копания в числах в структурный анализ.</p><p>Использование Seaborn в Python-стеке помогает выделиться среди разработчиков, которые ограничиваются Pandas и текстовыми логами, ведь визуализация часто позволяет быстрее заметить закономерности и убедить команду или заказчика в правильности гипотезы.</p><p>Seaborn также учит пониманию данных через визуальные паттерны, что улучшает навыки построения моделей машинного обучения (так понятнее, какие признаки важны), и помогает создавать наглядные отчёты для продуктовых решений, где результат анализа нужно доносить до людей не из айти-индустрии.</p><p><i>А какими библиотеками пользуетесь вы? Делитесь в комментариях!</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Архитектура BFF (Backend for Frontend): зачем нужна прослойка</title>
      <link>https://tproger.ru/articles/arhitektura-bff--backend-for-frontend---zachem-nuzhna-proslojka</link>
      <comments>https://tproger.ru/articles/arhitektura-bff--backend-for-frontend---zachem-nuzhna-proslojka?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/arhitektura-bff--backend-for-frontend---zachem-nuzhna-proslojka</guid>
      <description><![CDATA[<p>Что такое архитектура BFF. Показываем, зачем нужна прослойка Backend for Frontend. Рассматриваем преимущества и основные нюансы ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/arhitektura-bff--backend-for-frontend---zachem-nuzhna-proslojka">Архитектура BFF (Backend for Frontend): зачем нужна прослойка</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Android]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[iOS]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Стартапы]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[App Store]]></category>
      <category><![CDATA[CSR]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Spotify]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[NFT]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 30 Jun 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Представьте ситуацию: ваш REST API для CRM-системы отлично работает с веб-версией. Создаёте мобильное приложение для курьеров и упираетесь в стену. Эндпоинт заказов тащит 40 лишних полей с финансовой отчётностью, а нужной геолокации складов нет.</p><p>Может плодить новые эндпоинты или заставлять мобилку делать несколько запросов вместо одного? Каждый запрос жрёт трафик и батарею!</p><p>Элегантное решение — <b>архитектура Backend for Frontend (BFF)</b>. Это прослойка между клиентскими приложениями и основным API, которая адаптирует данные под потребности конкретного клиента.</p><h2>Основная идея backend for frontend</h2><p>Один API не может эффективно обслуживать разные типы клиентов. Сайт, приложение для iOS, Android, умные часы — у каждого свои потребности в данных, ограничения по производительности и особенности интерфейса.</p><p>Монолитный API создают с расчётом на универсальность — на практике это приводит к компромиссам. Веб-версии нужны данные для сортировки, мобильному приложению — минимальный набор для экономии трафика.</p><p><b>Следуя архитектуре BFF, вы можете создать логику для каждого типа клиента и не засорять основной API.</b> Вместо одного эндпоинта <i>/api/products</i>, который пытается угодить всем, появляются слои:</p><ul><li>один — оптимизирует данные для веба,</li><li>второй — для мобильных устройств,</li><li>третий — для умных часов.</li></ul><p>Обычно данные приходят в неудобном виде: несколько связанных сущностей нужно запрашивать отдельно и склеивать на клиенте. BFF берёт эту работу на себя.</p><p>Прослойка знает, что мобильному приложению нужны цены в рублях с округлением до целых, а веб-версии — точные значения в долларах. Для списка товаров мобилке достаточно названия и цены, а десктопной версии нужны ещё категории, рейтинги и количество отзывов.</p><p><b>Каждый клиент получает данные в том виде, в котором может их сразу отобразить</b>. Вместо загрузки 50 полей, из которых используется 5, BFF отдаёт только нужные данные.</p><p>«Можете добавить поле user_avatar в ответ?»</p><p>—<i> «Это сломает мобилку».</i></p><p>«Тогда сделайте отдельный эндпоинт».</p><p>—<i> «У нас нет времени».</i></p><p>С BFF этого диалога нет. Фронтенд-команда получает свой API и крутит его, как хочет.</p><p>Мобильное приложение съедает 10к запросов в секунду? Пишите BFF на Go. Веб-версию делает стажёр, который знает только JavaScript? Ставьте Node.js. Никто не заставляет выбирать одну технологию на все случаи жизни.</p><h2>Как работает backend for frontend (BFF)</h2><p>BFF размещается между клиентскими приложениями и основными бэкенд-сервисами, выполняя роль посредника. В отличие от API Gateway, который просто перенаправляет запросы, BFF трансформирует данные.</p><h3>Архитектура взаимодействия</h3><p>Классическая схема выглядит так: мобильное приложение обращается к своему BFF, веб-приложение — к своему, умные часы — к третьему. Каждый BFF знает особенности своего клиента и общается с основными сервисами на их «языке».</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-06-06/a765d5ae-2c66-4a1a-83fd-2f977ac31fa2.jpg" alt="" /><figcaption>Прослойка между клиентами и API</figcaption></figure><p>Когда мобильное приложение запрашивает список заказов, его BFF делает несколько вызовов к микросервисам:</p><ul><li>берёт базовую информацию о заказах,</li><li>подтягивает данные о товарах,</li><li>получает статусы доставки.</li></ul><p>Затем склеивает всё в один ответ, отбрасывая ненужные поля и добавляя вычисляемые значения.</p><p>Веб-версия для того же списка заказов получит расширенную информацию: подробные описания товаров, историю изменений статусов, данные для аналитики.</p><h3>Обработка и агрегация данных</h3><p>BFF не просто перекладывает данные из одного формата в другой. Он выполняет бизнес-логику.</p><p><i>Например, мобильный BFF может кешировать часто запрашиваемые данные, чтобы уменьшить количество сетевых запросов.</i></p><p>Если API возвращает цены в центах, мобильный BFF конвертирует их в рубли и округляет для отображения. Веб-версия получает точные значения с копейками для расчётов.</p><h3>Независимость и масштабирование</h3><p>Когда нагрузка на приложение растёт, масштабируется только его BFF. Проблемы с веб-версией не влияют на работу мобильных клиентов.</p><h2>4 ключевых преимущества BFF</h2><h3>Оптимизация передачи данных</h3><p>Самое очевидное преимущество — экономия трафика. Приложение не тащит 2 МБ JSON с полным каталогом товаров на мобильное устройство? BFF отдаёт только нужные поля.</p><p>Количество запросов тоже сокращается. Например, чтобы показать профиль пользователя, фронтенд делает 5 запросов:</p><ul><li>за основными данными,</li><li>аватаром,</li><li>списком друзей,</li><li>последними постами,</li><li>настройками приватности.</li></ul><p>BFF объединяет всё в один запрос, получая данные параллельно от разных сервисов.</p><h3>Упрощение фронтенда</h3><p>Половина фронтенд-кода уходит на трансформацию ответов API:</p><ul><li>парсинг дат,</li><li>группировку массивов,</li><li>вычисление производных значений.</li></ul><p>BFF может взять эту работу на себя.</p><h3>Безопасность через изоляцию</h3><p>BFF создаёт барьер между клиентами и сервисами. Мобильное приложение никогда напрямую не обращается к БД пользователей или платёжке — только через свой BFF.</p><p>Можно настроить разные уровни доступа:</p><ul><li>мобильный BFF видит только публичные данные,</li><li>API для партнёров работает в песочнице.</li></ul><p>Если мобильное приложение скомпрометировано, злоумышленник не получит доступ к внутренним сервисам.</p><h3>Независимое масштабирование</h3><p>Когда приложение попадает в топ App Store, нагрузка взлетает в разы. Но страдает только мобильный BFF — веб-версия продолжает работать стабильно. Можно быстро поднять дополнительные серверы только для мобильного трафика.</p><p>Появляется возможность экспериментировать с технологиями без риска. Хотите попробовать GraphQL для веб-версии? Внедряйте в один BFF. Тестируете новую базу данных? Подключайте к экспериментальной прослойке, не трогая продакшн.</p><h2>Когда стоит использовать backend for frontend</h2><p>BFF — инструмент для конкретных ситуаций.</p><h3>Когда интерфейсы кардинально отличаются</h3><p>Если ловите себя на мысли: <i>«этот эндпоинт нужен только для веба»</i> или <i>«мобилка использует 10% полей из ответа»</i>, — пора задуматься о BFF.</p><p>Красный флаг — когда фронтенд-разработчики начинают писать костыли для обработки «неудобных» данных. Если половина JavaScript-кода занимается парсингом и трансформацией ответов API, что-то пошло не так.</p><h3>Когда интерфейсы эволюционируют быстрее джунов</h3><p>Стартапы и продукты в активной фазе развития меняют интерфейсы каждую неделю.</p><p>Классическая проблема: дизайнеры придумали новый способ отображения товаров в каталоге. Теперь нужны дополнительные поля, другая группировка, новые фильтры.</p><p>Без BFF это означает изменения в основном API, которые могут сломать другие клиенты. С BFF — правки только в одном месте.</p><h3>Когда команды работают независимо</h3><p>Если у вас несколько фронтенд-команд, которые постоянно конфликтуют из-за API, BFF даст им свободу.</p><p>Команды получат свой API, который смогут развивать в нужном темпе. Это важно в больших компаниях, где бэкенд не успевает обрабатывать запросы от всех фронтендеров.</p><p>BFF распределяет ответственность: каждая команда поддерживает свой слой.</p><h3>Когда НЕ стоит использовать BFF</h3><p>Если у вас простое приложение с одним клиентом, BFF добавит лишнюю сложность. Если API уже идеально подходит всем клиентам, зачем что-то менять?</p><h2>3 типичные ошибки при внедрении BFF</h2><h3>Дублирование логики</h3><p>Начинается незаметно: мобильный и веб BFF нуждаются в одинаковой валидации пользователей. Разработчик копирует функцию из одного проекта в другой. Через полгода одинаковый код валидации живёт в четырёх местах, и каждое изменение превращается в квест.</p><p>Хуже, когда дублируется бизнес-логика. Расчёт скидок, обработка промокодов, правила доступа — это должно жить в основных сервисах, а не размазываться по BFF-слоям.</p><h3>Избыточная сложность вместо упрощения</h3><p>Пример: команда создаёт «универсальный BFF-фреймворк» с конфигурацией через YAML, поддержкой плагинов и собственным DSL. В итоге простое добавление поля в ответ требует изучения документации на 50 страниц.</p><p>Другая крайность — микро-BFF для каждой мелочи. Отдельный слой для авторизации, отдельный для форматирования дат, отдельный для валидации.</p><h3>Неправильная гранулярность</h3><p>Один BFF на все мобильные платформы может быть слишком общим: iOS и Android имеют разные особенности интерфейса. Но отдельный BFF для каждой версии приложения — явный перебор.</p><p>Частая ошибка — создание BFF по организационному принципу, а не по техническому. У нас три фронтенд-команды, значит нужно три прослойки. Но если все команды работают с похожими данными и интерфейсами, логичнее объединить усилия.</p><h2>Практические примеры использования BFF</h2><h3>Netflix</h3><p>Компания <a href="https://netflixtechblog.com/seamlessly-swapping-the-api-backend-of-the-netflix-android-app-3d4317155187">сделала</a> разные API для веб-версии, мобильных приложений, Smart TV и игровых консолей. Каждый BFF оптимизирован под особенности устройства, например, TV-версия предзагружает больше контента из-за медленной навигации пультом.</p><h3>Spotify</h3><p><a href="https://developer.spotify.com/documentation/web-api">Используют</a> BFF для разных клиентов: веб-плеер, мобильные приложения, десктопное приложение. Мобильный BFF агрессивно кеширует данные для офлайн-режима, веб-версия работает в реальном времени.</p><h3>SoundCloud</h3><p>Публично <a href="https://developers.soundcloud.com/blog/service-architecture-1">описывали</a> переход на BFF-архитектуру. У них отдельные слои для веб-версии и мобильных приложений, которые по-разному обрабатывают аудиопотоки и метаданные треков.</p><h2>Заключение</h2><p>Страдают все, когда один API пытается обслуживать веб-версию, мобилки и что-то ещё. Фронтенд получает неудобные данные, бэкенд обрастает костылями, пользователи — медленными приложениями.</p><p>Backend for Frontend создаёт слой между клиентами и основными сервисами. Каждый тип устройства получает API, заточенный под его потребности.</p><p>Внедряйте BFF, когда интерфейсы кардинально отличаются, продукт быстро развивается, а текущий API снижает производительность.</p><p>Ты уже программист, если читаешь это! Больше про кодинг — <a href="https://t.me/+a1v-IRDDUqI0MDhi">здесь</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>n8n: установка, настройка и интеграция с Python, Node.JS и PHP</title>
      <link>https://tproger.ru/articles/n8n--ustanovka--nastrojka-i-integraciya-s-python--node-js-i-php</link>
      <comments>https://tproger.ru/articles/n8n--ustanovka--nastrojka-i-integraciya-s-python--node-js-i-php?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Кирилл Косолапов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/n8n--ustanovka--nastrojka-i-integraciya-s-python--node-js-i-php</guid>
      <description><![CDATA[<p>Подробный туториал по установке и настройки n8n. Примеры интеграции с Python, Node.JS и PHP и взаимодействия с LLM Mistral AI.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/n8n--ustanovka--nastrojka-i-integraciya-s-python--node-js-i-php">n8n: установка, настройка и интеграция с Python, Node.JS и PHP</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[C#]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[iOS]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Opera]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[DeFi]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 29 Jun 2025 09:00:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>n8n — open-source платформа для автоматизации рабочих процессов (workflow), позволяющая создавать сложные цепочки задач без глубоких знаний программирования.</p><p><b>В статье рассмотрим:</b></p><p>- Установку локально и в облаке;</p><p>- Интеграцию с Python, Node.js и PHP;</p><p>- Примеры автоматизаций;</p><p>- Интеграцию с AI Mistral.</p><h2>Установка n8n</h2><h3>Локальная установка</h3><p>Нам потребуется Docker, проверьте установку:</p><p><b>Шаги установки:</b></p><p>1. Создайте том данных:</p><p>2. Запустите контейнер:</p><p>После запуска откройте: `http://localhost:5678`</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/32713acd-811d-40dc-8141-4fb625cd4216.png" alt="" /></figure><h3>Установка на удаленном сервере</h3><p>Развертывание мы произведем в облаке <a href="https://amvera.ru/n8n">Amvera</a>, так как в нем n8n предоставляется как преднастроенный сервис с бесплатным доменом (он нам нужен), настроенными переменными и проксированием до заблокированных в РФ LLM (OpenAI, Gemini, Claude и др.).</p><p>1. Регистрируемся на <a href="https://amvera.ru/n8n">Amvera</a>;</p><p>2. Выбираем n8n в плитке на главной странице;</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/e74d96ee-5f96-4c61-bf7a-562a38edc703.png" alt="" /></figure><p>3. Вводим название для проекта и выбираем тариф.</p><p><b>Готово, через 30 секунд запустится n8n с выделенным доменом и настроенными основными переменными.</b></p><h2>Настройка n8n</h2><h3>Первоначальная настройка</h3><p>1. Откройте n8n (локально: `localhost:5678`, в облаке: ваш домен)</p><p>2. Заполните данные администратора:</p><ul><li>Email</li></ul><ul><li>Имя/Фамилия</li></ul><ul><li>Пароль</li></ul><p>3. По желанию вы можете получить бесплатный лицензионный ключ:</p><ul><li>Введите email → "Send me a free license key"</li></ul><ul><li>Активируйте в разделе Settings → Usage</li></ul><h3>Настройка для HTTP</h3><p>1. Создайте новый <b>workflow</b> → Start from scratc</p><p>2. Добавьте триггер: <b>Webhook</b> - <b>On webhook call</b></p><p>3. Настройте:</p><ul><li>HTTP Method: POST</li></ul><ul><li>Path: `/n8n` (пример)</li></ul><ul><li>Respond Mode: Using 'Respond to Webhook' node</li></ul><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/6e51fb08-a901-408c-833e-eb27c3ab1f17.png" alt="" /></figure><p>4. Добавьте обработчик: <b>Core</b> → <b>Respond to Webhook</b></p><p>5. Настройте ответ:</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/37a5add7-0873-42e1-9004-d33a228d828b.png" alt="" /></figure><h2>Примеры использования</h2><h3>Пример на Python</h3><p>В примере на Python мы сделаем калькулятор. Суть: отправляем выражение через POST,  n8n делает вычисление, возвращаем результат.</p><p><b>Workflow</b>:</p><p>1. Добавьте <b>Core</b> → <b>Code node</b> между Webhook и Respons:</p><p><b>Клиент (Python):</b></p><p>Запустим код и посмотрим результат:</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/c9a2bd14-0a7e-4c6f-8544-e4a098a5cd5e.png" alt="" /></figure><h3>Пример на PHP</h3><p>В примере на PHP мы сделаем валидацию данных. Суть: отправляем данные через POST, n8n проверяет данные, возвращает результат.</p><p><b>Workflow (Code node):</b></p><p><b>Клиент (PHP):</b></p><p>Можем зайти на нашу страницу, все работает нормально:</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/722241bc-9794-41eb-9d32-8d73d7147800.png" alt="" /></figure><h3>Пример на Node.js</h3><p>Примером на node.js будет фильтр запрещенных слов. Суть: отправляем текст, n8n проверяет на наличие плохих слов, возвращает результат.</p><p><b>Workflow (Code node):</b></p><p><b>Клиент (Node.js):</b></p><p>Перед запуском установим библиотеку командой `npm install axios`</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/c56f275a-8c74-49bf-80e5-22d5becd222a.png" alt="" /></figure><h3>Интеграция n8n с Mistral AI</h3><p>Мы интегрируем нейросеть Mistral в телеграмм-бота на C# с помощью n8n. Перед тем как начать, нам нужно получить токен.</p><p>1. Зарегистрируйтесь на <a href="https://mistral.ai/">Mistral</a></p><p>2. Создайте агента <b>Сreate an agent</b></p><p>3. Перейдите в раздел <b>API Keys</b></p><p>4. Создайте и скопируйте API-ключ (звездочки наложены):</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/7bb95f2e-0ebe-47af-9089-5e10ee2822cf.png" alt="" /></figure><p>Теперь нужно создать credentials для работы с созданной моделью. Для этого в правом верхнем углу нажмите <b>Create credentials</b>. В списке найдите <b>Mistral Cloud API</b>. В открывшемся окне вставьте скопированный ключ и сохраните.</p><p>Осталось настроить workflow.</p><p>1. Добавляем <b>Webhook</b>:</p><ul><li>Method: POST</li></ul><ul><li>PATH: n8n</li></ul><ul><li>Respond: Using 'Respond to Webhook' Node</li></ul><p>2. Добавляем <b>AI Agent</b>:</p><ul><li>Source for Prompt: Define below</li></ul><ul><li>Prompt: `{{ $json.body.text }}`</li></ul><p>3. Подключаем <b>Chat Model </b>к созданному агенту:</p><ul><li>Credential to connect with: Mistral Cloud API</li></ul><ul><li>Model: mistral-large-2411</li></ul><p>4. Добавляем <b>Respond to Webhook</b></p><p>Вот так выглядит готовый workflow:</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/b2618a94-f3eb-4055-9061-ab185aca9d9d.png" alt="" /></figure><p>Можем приступить к созданию бота. Для начала введите эти команды по очередности:</p><p>Переходим в папку с кодом и редактируем файл `Program.cs`:</p><p>Запустите бота с помощью команды `dotnet run`.</p><h3>Автоматизируем с помощью n8n</h3><p>В примере автоматизации мы сделаем бота, который будет отправлять уведомления при заполнении формы, полностью без кода.</p><p>Для работы с <b>Telegram Node</b> понадобиться создать credentials <b>Telegram API</b>. Туда вставляем токен бота, полученный в @BotFater. Сохраняем и переходим к настройке workflow.</p><p><b>Создаем новый workflow. </b></p><p>1. Добавляем <b>On form submission</b>. В качестве примера я создам самую простую форму:</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/3bd6f188-16bd-4fa4-bde2-f7a88855bdad.png" alt="" /></figure><p>Перейдите по ссылке в <b>Producrion URL</b> чтобы заполнить форму.</p><p>2. Добавляем <b>Telegram Node</b>:</p><ul><li>Credential to connect with: Telegram account</li></ul><ul><li>Resource: Message</li></ul><ul><li>Operation: Send message</li></ul><ul><li>Chat ID: вставьте свой telegram id</li></ul><p>Text:</p><p>Готово! При новых заявках бот будет присылать уведомления.</p><h3>Деплой бота в Amvera Cloud</h3><p>Перед тем как начать деплой, мы должны создать конфигуационнный файл `amvera.yml`. Для этого создаем его в рабочем каталоге с ботом и вводим следующее:</p><p>Строго говоря, этот файл проще создать в конфигураторе в интерфейсе.</p><p>2. Структура проекта будет такой:</p><p>tg-bot/</p><p>├── Program.cs</p><p>├── tg-bot.csproj</p><p>├── amvera.yml</p><p>├── bin/</p><p>└── obj/</p><p>Идем В Amvera Cloud и создаем приложение <b>Приложения</b> — <b>Создать приложение</b>. Вводим название и выбираем тариф.</p><p>Далее загружаем все файлы, что есть у нас в каталоге, с ботом. В конце будет окно с настройкой конфигуцрации, выглядит оно так:</p><figure><img src="https://media.tproger.ru/user-uploads/115814/2025-06-16/7806bf2f-38e5-4aa4-ade2-6c12d3f6c214.png" alt="" /></figure><p>Нажимаем <b>Завершить</b> и ждем когда приложение будет запущено.</p><h2>Заключение</h2><p>n8n — мощный инструмент для создания интеграций и автоматизаций. Надеюсь, статья была вам полезна и буду рад обсудить в комментариях любые вопросы!</p>]]></content:encoded>
    </item>
    <item>
      <title>Обзор RBAC Wizard — инструмента для анализа и визуализации конфигурации RBAC в кластере Kubernetes</title>
      <link>https://tproger.ru/articles/obzor-rbac-wizard---instrumenta-dlya-analiza-i-vizualizacii-konfiguracii-rbac-v-klastere-kubernetes</link>
      <comments>https://tproger.ru/articles/obzor-rbac-wizard---instrumenta-dlya-analiza-i-vizualizacii-konfiguracii-rbac-v-klastere-kubernetes?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Лесных Анна]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/obzor-rbac-wizard---instrumenta-dlya-analiza-i-vizualizacii-konfiguracii-rbac-v-klastere-kubernetes</guid>
      <description><![CDATA[<p>Обзор познакомит с RBAC Wizard — удобным инструменте для тех, кому периодически нужно проводить аудит прав доступа в кластере Kubernetes. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/obzor-rbac-wizard---instrumenta-dlya-analiza-i-vizualizacii-konfiguracii-rbac-v-klastere-kubernetes">Обзор RBAC Wizard — инструмента для анализа и визуализации конфигурации RBAC в кластере Kubernetes</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 20 Jun 2025 14:24:23 GMT</pubDate>
      <content:encoded><![CDATA[<blockquote>Будь собой, остальные роли заняты.</blockquote><p>Меня зовут Юрий Дубовик, я DevOps-инженер компании <a href="https://flant.ru/?utm_source=web&amp;utm_medium=tproger&amp;utm_campaign=rbac_wizard">«Флант»</a>. Мы занимаемся DevOps-сопровождением — помогаем другим компаниям делать инфраструктуру надёжной и создавать комфортную среду для разработки.</p><p>Мы много работаем с Kubernetes, и иногда нашим инженерам нужно разобраться с настроенными правами доступа к разным объектам в K8s-кластере клиента. Приходится тратить много времени на сбор и упорядочивание информации, а потом сопоставлять, кому какие роли были назначены. Но сравнительно недавно появился Open Source-инструмент, который упрощает этот процесс, — RBAC Wizard.</p><p>RBAC Wizard позволяет быстро проанализировать конфигурации RBAC (Role-based access control) в кластере, а главное, может визуализировать всю собранную информацию. В этом обзоре я покажу, как установить этот инструмент с помощью Helm, и сравню его использование с ручным подходом.</p><h2>Зачем нужен аудит конфигураций RBAC</h2><p>RBAC — это механизм распределения прав доступа к объектам в Kubernetes-кластере. Подробно останавливаться на принципах работы RBAC я не буду, поскольку об этом уже написано множество материалов.</p><p>Вместо этого скажу пару слов о том, зачем нужен анализ конфигураций RBAC. Он помогает убедиться, что пользователи и сервисы имеют в кластере Kubernetes только те права, которые им действительно нужны для работы. Такой анализ позволяет выявить избыточные или ненужные привилегии, минимизировать риски случайных или злонамеренных действий, а также повысить безопасность и соответствие политике доступа в кластере.</p><h2>Что такое RBAC Wizard</h2><p><a href="https://github.com/pehlicd/rbac-wizard">RBAC Wizard</a> — это инструмент на Go с открытым исходным кодом, который помогает анализировать и визуализировать конфигурации RBAC вашего кластера Kubernetes. Он обеспечивает табличное и графическое представление объектов RBAC Kubernetes.</p><p>Хотя проект появился на GitHub недавно, в 2024 году, он уже успел привлечь интерес сообщества. Сейчас у RBAC Wizard чуть больше 270 звёзд.</p><h2>Как установить RBAC Wizard</h2><p>RBAC Wizard можно установить локально, запустить в контейнере или развернуть в кластере с помощью Helm. Процесс просто и понятно описан <a href="https://github.com/pehlicd/rbac-wizard?tab=readme-ov-file#how-to-install">в самом репозитории</a>.</p><p>Я выбрал вариант установки с помощью Helm:</p><p>После установки в кластере Kubernetes в пространстве имён rbac-wizard появится одноимённый ингресс rbac-wizard:</p><p>У ингресса указан хост rbac-wizard.local. Заменяем его на нужный нам адрес — домен, который находится под нашим управлением, например new.example.com. По этому адресу мы потом увидим RBAC Wizard в браузере.</p><p>Это всё! Больше никаких настроек не требуется. У меня на установку ушло 5–10 минут.</p><h2>Подготовка тестовых ролей</h2><p>Чтобы сравнить между собой решение задачи по анализу конфигураций в Kubernetes-кластере без RBAC Wizard и с ним, создадим тестовые ServiceAccount, ClusterRole и ClusterRoleBinding. Разумеется, если вы используете инструмент в готовом кластере, этот шаг стоит пропустить:</p><h2>Анализ конфигурации RBAC без RBAC Wizard</h2><p>Давайте посмотрим, как можно решить задачу без RBAC Wizard.</p><p>Если требуется найти пользователя или ServiceAccount, которые имеют определённую роль, выполняем следующий запрос:</p><p>Пример ответа:</p><p>Если же требуется найти все роли, которые закреплены за пользователем или ServiceAccount’ом, запрос будет таким:</p><p>Пример ответа:</p><p>Если мы получим большой объём ролей и связей с пользователями или ServiceAccount’ами, то придётся заносить все информацию в таблицу для дальнейшего анализа. Держать в голове все связи невозможно — такой метод сбора информации займёт очень много времени.</p><h2>Анализ конфигурации с помощью RBAC Wizard</h2><p>Теперь перейдём в браузере по указанному нами адресу в поле host спеки ингресса rbac-wizard и посмотрим, удобнее ли анализировать конфигурации с RBAC Wizard.</p><p>Интерфейс инструмента интуитивно понятный. В верхней его части находится RBAC Table. Как понятно из названия, здесь представлена информация о ClusterRoleBinding и RoleBinding в виде таблицы с возможностью поиска:</p><figure><img src="https://media.tproger.ru/user-uploads/115839/2025-06-19/19cb3346-1b53-4f31-88ec-055c215d2cb9.png" alt="" /><figcaption>Так отображается наша кластерная роль в таблице</figcaption></figure><p>Посмотреть подробности о том, какая роль кому назначена, можно, нажав на три вертикально расположенные точки в правой части таблицы:</p><figure><img src="https://media.tproger.ru/user-uploads/115839/2025-06-19/7b9825ce-bffb-4a12-af17-c9c98f1f695b.png" alt="" /></figure><p>Под таблицей отображается RBAC Map — граф с визуализацией объектов RBAC и их связей. В фильтре можно выбирать ClusterRoleBinding, RoleBinding и посмотреть их взаимосвязь с Role, ClusterRole, User или Group:</p><figure><img src="https://media.tproger.ru/user-uploads/115839/2025-06-19/baaf43bf-0004-4681-80bc-065f333896f3.png" alt="" /><figcaption>Так RBAC Wizard отображает связи нашей ClusterRole и ClusterRoleBindings</figcaption></figure><p>А так выглядят более сложные связи:</p><figure><img src="https://media.tproger.ru/user-uploads/115839/2025-06-19/29302185-1f28-438c-8059-4a05cefa667c.png" alt="" /><figcaption>Это один из наших тестовых кластеров</figcaption></figure><p>Привязку к подам утилита не показывает, то есть узнать, кто именно использует аккаунт, не получится.</p><p>Итого, RBAC Wizard позволяет мгновенно получить таблицу со всеми ClusterRoleBinding и RoleBinding, наглядно увидеть, кому и какие роли назначены, и быстро найти нужную информацию с помощью поиска. А граф может пригодиться в случаях, когда связей много.</p><h2>Заключение</h2><p>Буду краток. RBAC Wizard — простой как молоток инструмент. Но он неплохо упрощает жизнь, позволяя быстро проводить аудит ролей в кластере Kubernetes.</p><p>Из тех возможностей, которых пока не хватает, хотелось бы иметь фильтрацию по графу по ключевым словам, а не по точному названию. Также была бы полезна кликабельность графа, при которой описание объектов открывалось бы во всплывающем окне либо отображалась бы какая-то взаимосвязь графа с информацией из таблицы. Ну и будет здорово, если в инструменте появится ранжирование ролей по уровню доступа.</p><h2>Минутка рекламы</h2><p>Если перед вами стоят задачи по переходу на Kubernetes, внедрению DevOps-практик или сокращению Time to Market, а своих ресурсов и опыта недостаточно, обращайтесь к нам во <a href="http://flant.ru/services/devops-as-a-service/?utm_source=web&amp;utm_medium=tproger&amp;utm_campaign=rbac_wizard_ad">«Флант»</a>. Мы внедрим проверенные технологии, возьмём на себя полный цикл работ и будем сопровождать вашу инфраструктуру силами выделенной команды с гарантиями по SLA.</p>]]></content:encoded>
    </item>
    <item>
      <title>Почему ваше приложение тормозит: архитектурные bottlenecks, которые никто не замечает</title>
      <link>https://tproger.ru/articles/pochemu-vawe-prilozhenie-tormozit--arhitekturnye-bottlenecks--kotorye-nikto-ne-zamechaet</link>
      <comments>https://tproger.ru/articles/pochemu-vawe-prilozhenie-tormozit--arhitekturnye-bottlenecks--kotorye-nikto-ne-zamechaet?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pochemu-vawe-prilozhenie-tormozit--arhitekturnye-bottlenecks--kotorye-nikto-ne-zamechaet</guid>
      <description><![CDATA[<p>Как найти и устранить архитектурные bottleneck'и: причины тормозов, типовые ошибки и пошаговая методика диагностики.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pochemu-vawe-prilozhenie-tormozit--arhitekturnye-bottlenecks--kotorye-nikto-ne-zamechaet">Почему ваше приложение тормозит: архитектурные bottlenecks, которые никто не замечает</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Дата-центр]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[OpenTelemetry]]></category>
      <category><![CDATA[Тимлид]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 19 Jun 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ваше приложение тормозит, хотя сервер мощный, код вроде нормальный, а метрики — не очень-то и загружены? Возможно, вы столкнулись с архитектурным bottleneck'ом — скрытым ограничением, которое убивает производительность под нагрузкой. Вместе с Никитой Ульшиным, тимлидом команды разработки архитектурных инструментов в Т-Банк и автором тг-каналов <a href="https://t.me/ulshinblog">Никита Ульшин про IT</a> и <a href="https://t.me/tech_fit">ТехнофITнес | Никита Ульшин</a>, разбираем типовые узкие места в системах, от неэффективной аллокации до однопоточных участков, и показываем пошаговый подход к их поиску и устранению.</p><h2>Почему «тормоза» — это не всегда про код</h2><p>Когда пользователь жалуется на «медленное приложение», большинство разработчиков по привычке лезет в код. Профилируем, ищем неэффективные циклы, оптимизируем до запятых. Но в реальности причина часто не в логике, а в том, что её окружает.</p><p>Современное приложение — это сложный организм: десятки микросервисов, базы данных, кэши, брокеры, сети. И тормозить может любой из этих компонентов. А ты в это время профилируешь ни в чём не виноватый for.</p><p>Вот что тормозит, когда код ни при чём:</p><p><b>Архитектура</b></p><ul><li>Цепочки вызовов: каждый хоп — +latency.</li><li>Синхронные зависимости: завис один сервис — тормозят все.</li><li>Общие ресурсы: shared state, глобальные очереди, блокировки.</li></ul><p><b>Базы данных</b></p><ul><li>Нет индексов или плохой план запроса.</li><li>N+1-запросы — особенно при неосторожной ORM.</li><li>Частый доступ к одним таблицам — гонка за IO и блоки.</li><li>Нет шардинга или репликации в масштабируемой нагрузке.</li></ul><p><b>Сеть</b></p><ul><li>Высокая задержка между регионами (особенно multi-cloud).</li><li>Потери пакетов, DNS-проблемы, нестабильность.</li><li>Отсутствие connection reuse: TLS-handshake на каждый вызов.</li></ul><p><b>Очереди и брокеры</b></p><ul><li>Один медленный потребитель тормозит всех.</li><li>Нет защиты от от backpressure или дедупликации.</li><li>Неправильные batch- и ack-настройки.</li></ul><p><b>Аллокаторы и GC</b></p><ul><li>Фрагментация памяти, нагрузка на GC.</li><li>Финалайзеры, слабые ссылки, плохо настроенный heap.</li></ul><p><b>Конфигурации</b></p><ul><li>Маленький пул соединений — пул заканчивается, запросы ждут.</li><li>Агрессивные retry-политики — система перегружается.</li><li>CPU throttling в Kubernetes — контейнеру не хватает ресурсов.</li></ul><p><b>Сериализация и форматы</b></p><ul><li>Перегруженные JSON/Protobuf.</li><li>Вложенные base64 + gzip + JSON — боли сериализации.</li></ul><h3>Почему bottlenecks остаются невидимыми до продакшена</h3><p>Многие из них —  не баги, а последствия архитектурных решений. В дев-среде всё летает, потому что:</p><ul><li>мало данных,</li><li>нет конкуренции за ресурсы,</li><li>нет распределённой нагрузки.</li></ul><p>В проде начинается хаос. Рассмотрим на конкретных примерах:</p><ul><li>На деве — 2 запроса в секунду, в проде — миллион. Кто-то запустил маркетинговую рассылку, аналитик тащит big report, а клиент ретраит ошибки.</li><li>Архитектура «в лоб»: всё синхронно, каждый сервис вызывает по цепочке десяток других. Пока нагрузка мала — все живет. Как только одно звено не выдерживает — перестает жить.</li><li>Иллюзия масштабируемости: до 100 пользователей —  всё окей, на 1000 — каскадная деградация.</li><li>«Невидимая боль» между сервисами: каждый по отдельности быстрый, но блокирует друг друга на базе.</li></ul><h2>Слои архитектуры и где в них зарыты проблемы</h2><p>Когда приложение тормозит, мы часто копаемся в отдельных компонентах — фронте, бэке, базе. Но bottleneck может быть не внутри компонента, а между ними: в конфигурации, связях, сетевых задержках, очередях вызовов.</p><p>Чтобы понять, где искать узкое место, нужно пройтись по всей цепочке — от клиента до внешних сервисов.</p><h3>На уровне клиента</h3><ul><li>Тяжёлые бандлы. Огромный JS, куча аналитики, трекеры — и вот страница загружается 8 секунд на старом телефоне.</li></ul><ul><li>Чрезмерные запросы. После загрузки — 10 fetch в секунду. API захлёбывается, браузер — тоже.</li></ul><ul><li>Нет кеша. Даже статичные справочники каждый раз грузятся заново.</li></ul><ul><li>Лаги рендеринга. Бэкенд дал ответ за 100ms, но интерфейс лагает — сложные таблицы, графики, reflow.</li></ul><h3>На уровне API Gateway / BFF</h3><ul><li>Непрозрачная маршрутизация —  скрытые таймауты, ошибки ретраев.</li><li>Сборка ответов из микросервисов — цепочка из 5 вызовов на каждый клиентский запрос.</li><li>Синхронные вызовы без деградации — если один микросервис недоступен, падает весь endpoint.</li></ul><h3>На уровне Backend / бизнес-логики</h3><ul><li>Цепочки вызовов. Один сервис вызывает другой, тот — ещё один… И так далее.</li><li>Ограниченный пул соединений —  сервис готов работать, но все worker-ы ждут ресурсы.</li><li>Логика в цикле по данным —  N+1-запросы, сериализация, дублирование работы.</li></ul><h3>На уровне базы данных / кэша</h3><ul><li>Full table scan. Работает быстро на 1000 строках. Умирает на 10 миллионах.</li></ul><ul><li>Локи и гонка за ресурсы. Один UPDATE лочит строку, остальные ждут.</li></ul><ul><li>Кэш не промахивается, но и не помогает —  stale данные, повторные чтения, инвалидация не работают, как ожидалось.</li></ul><p><b>На уровне внешних сервисов (API, брокеры, платёжки)</b></p><ul><li>Без timeout и fallback. Внешний API залип — вы вместе с ним.</li><li>QPS-лимиты. Вы не знали, а вас уже зарейт-лимитили.</li><li>Нестабильная сеть. DNS тормозит, соединения рвутся, задержки скачут между регионами.</li></ul><p>Оптимизация кода важна. Но если система тормозит, искать нужно по всей цепочке —  от кнопки на фронте до брокера в соседнем дата-центре.</p><blockquote>Есть архитектурные паттерны риска, которые часто приводят к тормозам через полгода. Во-первых, общий ресурс на всех: один Redis, один Kafka-топик, одна таблица. Пока нагрузка маленькая — ок. Потом начинается бойня за доступ. Во-вторых, микросервисный монолит: формально разделены, но живут как один. Масштабировать невозможно. В-третьих, цепочки вызовов: 5–6 сервисов на путь одного запроса. Один подвис — и вся цепочка тормозит.</blockquote><h2>Базы данных: когда даже SELECT тормозит всё</h2><p>В бэкенде одна из самых коварных фраз — «ну это же просто SELECT, что с ним будет». Ответ — сначала ничего. Но со временем появляется 10 миллионов строк, и ваш innocuous SELECT превращается в full scan, который лочит диск, ест CPU и тормозит всё, что движется.</p><p>Типичная ситуация: в таблицу пишут JSONB без нормализации и индексов. Пока строк мало — жить можно. Но при росте объёмов каждый запрос превращается в медленное последовательное чтение. Это не просто «долго», а начинает мешать соседним транзакциям: подгружаются stale данные, вытесняется кэш, система начинает проседать вся — вплоть до API.</p><p>Отдельная ловушка — смешение OLTP и OLAP нагрузки. Днём — INSERT и UPDATE от клиентов, ночью — фоновая аналитика, высчитывающая отчёт за год с десятком JOIN и чтением всей таблицы. Если все запросы идут в один инстанс, происходит конкуренция за ресурсы: аналитика сбивает кеш, вызывает блокировки, а продакшн-метрики начинают сыпаться.</p><h3>Как понять, что тормоза идут из базы, а не из бэкенда?</h3><p>Вот несколько типичных признаков:</p><ul><li>Бэкенд «висит» на запросах к базе — в логах видно, что приложение ждёт результат запроса (долгий await, query(), findMany() и т. д.).</li></ul><ul><li>Latency растёт, а CPU и память в норме — сервис сам по себе не перегружен, но ответ приходит медленно.</li></ul><ul><li>В базе фиксируются медленные запросы — например, SELECT или JOIN занимают секунды, хотя раньше проходили за миллисекунды.</li></ul><p>Чтобы подтвердить гипотезу, можно:</p><ul><li>Включить логирование SQL-запросов с таймингом;</li><li>Посмотреть трейс: если шаг DB заметно длиннее остальных — это тревожный сигнал;</li><li>Запустить EXPLAIN ANALYZE на типовые запросы и проверить, где зарыта сложность.</li></ul><h3>Архитектура доступа к данным: как закладываются тормоза</h3><p>Архитектура доступа к данным — фундамент, на котором держится производительность всей системы. Даже идеальный алгоритм или быстрый backend не спасут, если данные извлекаются медленно, неэффективно или избыточно.</p><p>На что здесь стоит обратить внимание:</p><ul><li><b>Количество и характер обращений. </b>Один пользовательский запрос не должен порождать десятки обращений к базе. Планирование кэшей, агрегаций, prefetch, нормализация — всё это часть архитектуры.</li><li><b>Уровень абстракции над данными.</b> ORM может быть удобным, но часто скрывает десятки неэффективных SELECT’ов. Нужно понимать, какие запросы реально идут в базу, и не превращать каждый use case в потенциальный bottleneck.</li><li><b>Стратегия хранения и агрегаций.</b> Если вы пытаетесь на лету агрегировать 100 миллионов строк без шардинга и партиционирования — тормоза неизбежны. Архитектура должна учитывать нагрузку, частоту агрегаций, структуру данных.</li></ul><h2>Очереди и событийные системы: скрытые замедления</h2><p>Событийные архитектуры и очереди вроде Kafka или RabbitMQ часто воспринимаются как универсальное средство от всех проблем: «Сделаем асинхронно — и всё полетит». Но это не серебряная пуля. Очередь не решает проблему — она её откладывает. А иногда сама становится bottleneck’ом.</p><p>Типовые ситуации, когда очередь тормозит систему:</p><ul><li><b>Медленные консьюмеры.</b> Очередь принимает сообщения с высокой скоростью, но обработчики не успевают, и начинается накопление беклога. В результате задержки накапливаются, SLA летит, а вы только недоумеваете, почему всё так тормозит.</li><li><b>Неправильный размер batch’ей.</b> В Kafka и аналогах размер и частота batch'ей критичны. Слишком большие — ждём, пока соберётся партия. Слишком маленькие — тратим ресурсы на лишнюю загрузку и пересылку. В итоге получаем либо высокую задержку, либо низкую производительность.</li><li><b>Неравномерное партиционирование</b>. Если сообщения распределяются по партициям неравномерно, то часть узлов будет простаивать, а часть захлёбываться под нагрузкой. Одна горячая партиция может стать bottleneck’ом всей системы.</li><li><b>Проблемы с retry.</b> Если нет чёткой стратегии повторных попыток, логирования и DLQ (dead-letter queue), битое событие может крутиться в системе бесконечно, тормозя всё остальное. Также если что-то пошло не так и включились retry-механизмы, задержка может вырасти в разы. Особенно если есть backoff или дедлоки.</li></ul><h3>Почему «асинхронно» ≠ «мгновенно»</h3><p>Асинхронность — это не про скорость, а про отложенность. Событие отправлено — но когда оно будет обработано, никто не знает. Через секунду, через минуту, через час?</p><p>Типичные причины задержек в асинхронной обработке:</p><ul><li>Очередь перегружена, и событие просто стоит в хвосте.</li><li>Обработчик падает или рестартится, и событие ждёт.</li><li>Политики повторной обработки не настроены — событие крутится бесконечно.</li><li>Слишком много этапов внутри одного воркера: валидация, запись в БД, вызов API — всё это задержки, которые суммируются.</li></ul><h3>Что происходит, когда очередь переполнена — и почему это тяжело заметить</h3><p>Когда очередь переполняется, события в ней начинают задерживаться или теряться, в зависимости от конфигурации. Но хуже всего то, что это происходит тихо. Формально система работает: события принимаются, воркеры их обрабатывают. Но обработка начинает всё сильнее и сильнее отставать.</p><p>Очередь продолжает принимать события, но обрабатываются они с огромным отставанием. Новое сообщение встаёт в хвост и ждёт своей очереди десятки секунд или минут. Такую ситуацию легко не заметить, если у вас не настроен грамотный мониторинг.</p><p>Для защиты от переполнения очереди нужно мониторить несколько важных показателей:</p><ul><li>Глубина очереди (в Kafka — consumer lag, в RabbitMQ — queue.messages_ready или queue.messages): показывает, сколько сообщений накопилось и ждёт обработки.</li><li>Publish rate: сколько сообщений в секунду публикуется.</li><li>Processing rate: сколько сообщений в секунду обрабатывается.</li><li>Утилизация ресурсов воркеров (CPU, memory, I/O).</li><li>End-to-end latency: сколько времени проходит от публикации события до его обработки.</li></ul><blockquote>Есть самые частые причины накопления сообщений:<br /><br />1) Медленные потребители, которые не успевают обработать весь поток сообщений. <br />2) Шумная архитектура: 10 сообщений там, где можно было обойтись и одним. <br />3) Неправильное масштабирование брокера (например, неправильно выбранный ключ партиционирования в Kafka).</blockquote><h2>API и микросервисы: где тормозит взаимодействие</h2><p>Микросервисная архитектура на бумаге выглядит идеально: каждый сервис автономен, команды независимы, масштабирование прозрачно. Но на практике все часто превращается в болото синхронных вызовов, где каждый сервис делает запросы в пять других.</p><h3>Что тормозит в микросервисах?</h3><p><b>Много лишних вызовов</b></p><p>Один пользовательский запрос вызывает лавину внутренних HTTP/gRPC-запросов. Каждый из них — это:</p><ul><li>сетевые задержки (latency),</li><li>сериализация/десериализация данных,</li><li>ретраи при сбоях,</li><li>риск таймаутов и обрывов.</li></ul><p>Если таких вызовов много, задержка накапливается каскадом.</p><p><b>Цепочка зависимостей</b></p><p>Классика жанра: заказ зависит от оплаты, оплата — от биллинга, биллинг — ещё от трёх микросервисов.</p><p>Если тормозит хотя бы один, сыплется всё. Получается эффект домино и каскадные отказы, где неполадка в одном звене рвёт всю цепочку.</p><p><b>Нет fallback'а и таймаутов</b></p><p>Если вызов к нужному сервису не отвечает, а у вас нет fallback'а или таймаута — всё встаёт. Особенно плохо, если вызов синхронный и блокирует поток.</p><p><b>Наносервисы</b></p><p>Иногда архитекторы увлекаются и дробят сервисы до абсурда. Начинается с «давайте переиспользуем», а заканчивается 10 API-вызовами ради одного действия.</p><p>Вместо бизнес-логики система начинает заниматься оркестрацией — координирует сама себя.</p><h2>Кэш и CDN: почему «ускорители» тоже могут тормозить</h2><p>Кэш традиционно считается палочкой-выручалочкой для производительности. «Добавим кэш — и всё полетит», — думают команды, особенно под нагрузкой. Но в реальности кэш может не только не ускорить, но и наоборот — замедлить или даже уронить систему, если использовать его без понимания.</p><p>Вот несколько типичных ошибок, которые приводят к деградации производительности:</p><h3>Невалидные или слишком агрессивные стратегии</h3><p>Если кэш живёт слишком долго — пользователи получают устаревшие данные. Если обновляется слишком часто — создаются лишняя нагрузка на базу и почти постоянные «промахи».</p><p>А если кэш сбрасывается при каждом изменении, он вообще не успевает выполнять свою роль.
В итоге: нестабильное поведение, непредсказуемая производительность и раздражённые пользователи.</p><h3>Перегрузка кэша</h3><p>Когда кэш настолько загружен, что сам по себе начинает тормозить — это уже не помощь, а вред. Такое часто случается с Redis, Memcached или in-memory решениями, особенно если они:</p><ul><li>работают на одной машине без шардинга;</li><li>содержат тяжёлые объекты;</li><li>используются как универсальный ответ на все проблемы.</li></ul><p>В таких случаях кэш из «ускорителя» может падать под нагрузкой быстрее, чем база данных.</p><h3>Кэш-миссы и лавины запросов</h3><p>Классическая проблема: TTL у популярных ключей истекает одновременно, и тысячи запросов идут мимо кэша в базу.
Это может случиться после деплоя, сброса кэша или при пиковой нагрузке.</p><p>Вместо одного запроса вы получаете 10 000, и тормозит всё. Особенно критично это для систем с CDN, где внезапное истечение кэша популярных страниц может вызвать DDoS-подобную нагрузку.</p><h3>Несогласованность кэшей</h3><p>Если в системе несколько уровней кэширования (CDN, фронтовый кэш, Redis) и они обновляются по-разному, возникает несогласованность. Один пользователь видит новые данные, другой — старые. Один сервис работает с актуальной версией, другой — с устаревшей. Начинается охота за фантомами: баги вроде есть, но не у всех.</p><h2>Вычисления и память: что не так с аллокацией</h2><p>Почему мощные сервера с десятками ядер и гигабайтами памяти тормозят? Потому что производительность упирается не в «железо», а в то, как именно это железо используется. От плохого управления памятью до блокирующих операций — в системе может быть множество узких мест, которые незаметно душат throughput.</p><h3>Частые и крупные аллокации</h3><p>Каждый раз, когда создаётся новый объект, память выделяется из кучи. При интенсивной работе (например, при парсинге JSON или обработке больших структур) это может происходить слишком часто, и тогда запускается сборщик мусора (GC).</p><p>Сборка мусора — это не бесплатная операция: она останавливает выполнение программы. Частые GC-паузы создают «плавающую» производительность, когда всё работает быстро, но время от времени подвисает даже на секунду.</p><h3>GC-паузы</h3><p>Автоматическое управление памятью — благо, но оно же и ловушка. В языках вроде Go, Java или Python невозможно точно контролировать, когда сработает сборщик. Даже задержка в 50 миллисекунд может ощутимо ударить по RPS или нарушить real-time обработку.</p><p>Некоторые компании придумывают обходные пути. Так, в Twitch <a href="https://blog.twitch.tv/en/2019/04/10/go-memory-ballast-how-i-learnt-to-stop-worrying-and-love-the-heap/">использовали</a> технику memory ballast для Go-приложений, чтобы стабилизировать поведение GC и избежать всплесков пауз.</p><h3>Синхронные и блокирующие операции</h3><p>Если операции с файлами, сетью или базой данных выполняются синхронно, поток блокируется — и в это время не может обрабатывать другие задачи. Это особенно критично, если такой поток обслуживает пользовательские запросы.</p><p>Классический пример — однопоточный web-сервер, который просто ждёт ответ от другой системы. Или библиотека, использующая mutex.Lock() в горячем участке, из-за чего десятки горутин встают в очередь.</p><h3>Ограниченные ресурсы и очереди</h3><p>Даже в многопоточном приложении можно легко создать узкое место. Например:</p><ul><li>доступ к объекту синхронизирован через mutex или RW-lock;</li></ul><ul><li>пул соединений имеет жёсткий лимит;</li></ul><ul><li>очередь задач не может масштабироваться под нагрузку.</li></ul><p>В таких случаях нагрузка растёт, а пропускная способность — нет. Всё упирается в искусственное ограничение, введённое «на всякий случай».</p><h3>Однопоточные горячие участки</h3><p>Даже в многопоточной архитектуре может оказаться, что 80% времени тратится в одном месте, которое не масштабируется — например, в процессе сериализации, расчёта или шифрования. Иногда этот участок реализован однопоточно (особенно в Node.js, Python или старом Go-коде) и становится главным тормозом системы. Под высокой нагрузкой это становится особенно заметно.</p><h2>Что делать: подход к поиску и устранению bottlenecks</h2><p>Когда система тормозит, возникает соблазн сразу «что-то сделать» — добавить кэш, увеличить ресурсы, ускорить базу. Но без системного подхода можно попасть в замкнутый круг, где проблема будет возвращаться снова и снова. Чтобы избежать этого, лучше использовать пошаговую стратегию: зафиксировать симптомы, локализовать проблему, подтвердить и только потом оптимизировать.</p><h3>Шаг 1. Зафиксировать симптомы и контекст</h3><p>Важно понять, что именно «тормозит» и в каких условиях. Типовые вопросы:</p><ul><li>Когда проявляется проблема: под нагрузкой, в пиковые часы, у конкретных пользователей?</li><li>Что именно работает медленно: API, отчёты, фоновая обработка?</li><li>Как это влияет на систему: задержки, таймауты, ошибки?</li></ul><p>Пример: пользователи жалуются, что отчёты стали строиться дольше. Вчера — 5 секунд, сегодня — 20.</p><h3>Шаг 2. Проверить очевидные метрики</h3><p>Начинаем с того, что уже доступно в мониторинге:</p><ul><li>латентность API (p50, p95, p99);</li><li>загрузка CPU, использование памяти и диска;</li><li>глубина очередей и backlog;</li><li>паузы GC;</li><li>кэш hit/miss ratio.</li></ul><p>Если латентность растёт, а CPU загружен на 20%, это может указывать на проблемы в синхронных вызовах, аллокации или неэффективной работе кэша.</p><h3>Шаг 3. Сузить область через трейсинг и логирование</h3><p>Чтобы выяснить, не «тормозит» ли другой сервис, используйте распределённый трейсинг (Jaeger, OpenTelemetry, Sentry Performance) или логирование таймингов внутри запроса.</p><p>Пример (Go, обёртка для http.RoundTripper):</p><p>Так можно увидеть, что, например, сторонний сервис отвечает 3 секунды, или Redis стал отдавать ответ за 300 мс вместо обычных 3 мс.</p><h3>Шаг 4. Воспроизвести нагрузку</h3><p>Если проблема зависит от объёма данных, воспроизведите реальный сценарий или нагрузку:</p><ul><li>используйте инструменты вроде k6, Locust, Artillery;</li><li>проверьте реальные условия (например, отчёт за 30 дней, а не за 3).</li></ul><p>Если латентность растёт нелинейно, это может быть признаком N+1-запросов, неоптимального SQL или проблем с аллокацией.</p><h3>Шаг 5. Подтвердить проблему профилированием</h3><p>Если есть подозрение на перегрузку CPU или утечку памяти, используйте профилировщики:</p><ul><li>Go — pprof;</li></ul><ul><li>Java — JFR, VisualVM;</li></ul><ul><li>Node.js — clinic.js, chrome://inspect.</li></ul><p>Пример (Go, локальный pprof):</p><p>Профиль покажет, где тратится CPU, как распределяются аллокации, как часто запускается GC и в каком коде.</p><h3>Шаг 6. Оптимизировать, перепроверить и зафиксировать</h3><p>После подтверждения проблемы можно переходить к исправлениям:</p><ul><li>кэшировать внешний вызов;</li></ul><ul><li>заменить тяжёлую сериализацию (например, на easyjson в Go);</li></ul><ul><li>убрать блокирующую операцию;</li></ul><ul><li>перейти на batch-обработку.</li></ul><p>После изменений обязательно перепроверьте метрики, чтобы убедиться, что оптимизация действительно дала эффект — и не добавила новых узких мест.</p><h2>Итоги: как не попасть в архитектурную ловушку</h2><p>Bottleneck’и возникают даже в самых технологичных проектах. Это не всегда следствие «плохого кода» — куда чаще они появляются из-за архитектурных просчётов: недооценили нагрузку, не предусмотрели масштабирование, сделали ставку на неправильное решение.</p><p>Главная проблема в том, что узкие места проявляются не сразу, а под нагрузкой. В разработке и тестах всё летает, потому что ресурсов хватает. А вот в проде — особенно при росте аудитории — на поверхность всплывает всё, что не выдерживает реального объёма данных или конкуренции за ресурсы.</p><p>Чтобы избежать таких проблем, важно не просто писать чистый код, а мыслить системно: проектировать архитектуру так, чтобы она могла жить под давлением. Вот несколько принципов, которые помогут.</p><h3>Думайте об объёмах и росте</h3><p>Архитектура должна выдерживать не только текущую нагрузку, но и разумный рост. Строить систему на «миллиард пользователей» с первого дня — избыточно. Но если вы рассчитываете, что пользователей станет в 5 раз больше через год — закладывайте это сейчас. Узкие места не любят сюрпризов.</p><h3>Настраивайте метрики с первого дня</h3><p>Без данных вы работаете вслепую. Любой анализ будет превращаться в гадание: виноват код или Redis, где тормозит — непонятно. С самого начала следите за ключевыми метриками: latency, очереди, GC, hit/miss в кэше, ошибки. Это не «доработка потом», а часть живой системы.</p><h3>Проектируйте с учётом деградации</h3><p>Любая система может начать тормозить — вопрос в том, как она себя при этом поведёт. Важно предусмотреть мягкую деградацию: таймауты, очереди, резервные сценарии, ограничение входящего трафика.</p><h3>Не усложняйте архитектуру без повода</h3><p>Микросервисы, шины, очереди, event-driven — всё это мощные инструменты. Но сложность должна быть оправданной. Простая, понятная архитектура с мониторингом и масштабируемыми точками зачастую выигрывает у перегруженной модульной схемы, которую никто не может отладить.</p><p>Bottleneck’и — это не баг, а симптом. И хороший архитектор — это не тот, кто устраняет тормоза, а тот, кто их не допускает.</p><p>Больше боли коддеров — <a href="https://t.me/+ezugB7gnIEsxNGMy">тут</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как встроить распознавание документов в Android: пошаговое руководство</title>
      <link>https://tproger.ru/articles/kak-vstroit-raspoznavanie-dokumentov-v-android--powagovoe-rukovodstvo</link>
      <comments>https://tproger.ru/articles/kak-vstroit-raspoznavanie-dokumentov-v-android--powagovoe-rukovodstvo?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Smart Engines]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-vstroit-raspoznavanie-dokumentov-v-android--powagovoe-rukovodstvo</guid>
      <description><![CDATA[<p>Разбираемся, как быстро добавить возможность распознавания документов в Android. Пошаговое руководство по встраиванию Smart Document Engine.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-vstroit-raspoznavanie-dokumentov-v-android--powagovoe-rukovodstvo">Как встроить распознавание документов в Android: пошаговое руководство</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[SEO]]></category>
      <category><![CDATA[Android]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Распознавание]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[BASIC]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Intel]]></category>
      <category><![CDATA[ICO]]></category>
      <category><![CDATA[Стартапы]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[XML]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 10 Jun 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Привет, Tproger!</p><p>Мы в <a href="https://smartengines.ru/">Smart Engines</a> занимаемся разработкой софта для распознавания самых разных документов — начиная от паспорта РФ, свидетельства о рождении и заканчивая первичкой вроде УПД или ТОРГ-12, а также банковских карт, номеров телефона и баркодов. Наши библиотеки написаны полностью с нуля (на плюсах), мы уделяем огромное внимание скорости алгоритмов, нейросетям (новым архитектурам, размеру и правильному применению) и оптимизации, за счет чего наш софт портируется на любые архитектуры и может быть использован на любой платформе.</p><p>Сегодня продолжим знакомиться через рассказ о нашем софте, на очереди вторая библиотека — Smart Document Engine и ее возможности работы с жесткими и гибкими формами.</p><h2>Гибкие и жесткие формы</h2><p>Вначале про сами формы: они могут быть «жесткими» и «гибкими». Жёсткие формы подразумевают, что положение всех объектов на форме может быть задано прямо в виде координат на шаблоне. Самое простое определение для жестких форм — они совпадают «на просвет». Возьмите пару листов А4 с распечатанной жёсткой формой, наложите друг на друга, и места расположения полей точно совпадут. Гибкие формы устроены гораздо сложнее, но все равно имеют свою характерную структуру и топологию.</p><p>Распознавание жестких форм можно свести к детекции формы на изображении и распознавании определенных областей, где должны быть искомые поля. Гибкие формы требуют гораздо более сложных систем поиска (к тому же, завязанных на результате предыдущих действий, например, OCR, что только увеличивает возможность ошибки).</p><p>Кстати, иногда вместо распознавания текста целиком достаточно просто ответить на вопрос «есть ли текст в выбранной области, и, если есть, то где»</p><p>Но не надо думать, что жёсткие формы совсем просты — большие белые поля без каких-либо символов (или одинаковый узор по краям, как это бывает с бланками гособразца), малый объём статического текста и некоторая вариативность бланков тоже заставляют потрудиться над детекцией и классификацией шаблонов.</p><p>Также существуют общие для подобных форм проблемы. Правильно интерпретировать галочки в чекбоксах, найти штрихкоды, правильно разметить табличные данные — есть куча проблем, каждая из которых имеет своё state-of-the-art решение и набор алгоритмов, над которыми нужно ломать голову.</p><h2>Почему не LLM, хотя казалось бы</h2><p>Сейчас мы переживаем бум развития нейросетей — генеративные и классифицирующие сети появляются как грибы после дождя. Количество задач, которые они могут решить, тоже кажется неисчислимым: казалось бы — дайте обучающую выборку побольше, и всё получится! Тем более, что примеры использования нейросетей для автоматизации рутины уже можно встретить на каждом шагу: об этом пишут заметки и обзорные статьи на научно-популярных ресурсах, а интеграторы и стартапы предлагают решения по созданию чат-ботов и помощников на основе ИИ, обученного на внутренней документации больших компаний.</p><p>Однако чем сложнее нейросеть, чем глубже степень обучения — тем выше шанс, что она начнёт бредить. Мы все какое-то время назад <a href="https://shedevrum.ai/post/bc5bf060107711eeb9ea06d64eab8f23/">смеялись</a> над шести-семипалыми героями очередных сгенерированных изображений, сейчас посмеиваемся над сгенерированными сетями текстами с описанием несуществующих фильмов и книг, но смешно ли будет нам (а особенно бухгалтерии), если нейросеть начнет галлюцинировать при распознавании платёжных реквизитов или суммы НДС? И чем выше степень развития сетей — тем менее заметными будут становиться такие ошибки.</p><p>В прошлом году на одной из ключевых конференций в области анализа и распознавания документов — ICDAR — учёные традиционно задались вопросом о будущем OCR. И сошлись во мнении, что OCR нисколько не устарела, благополучно развивается и остается наиболее надежным инструментом распознавания. Обеспечить требования консистентности (и ещё всякого такого) информации, извлекаемой из изображения, все равно сможет только старый добрый OCR и прочие детерминированные алгоритмы. И именно их разработкой (и доведением до совершенства) мы и занимаемся.</p><p>Вернёмся к библиотеке <a href="https://smartengines.ru/intelligent-document-recognition/">Smart Document Engine</a>. Как уже говорилось выше, гибкие формы на то и гибкие, что исключительно геометрией на них не обойдёшься — вопрос местонахождения полей решается «на лету». На процесс взаимодействия с библиотекой это влияет в самом конце, на моменте работы с результатом. Перейдем к знакомству с интерфейсом на примере всё того же встраивания в андроид.</p><h2>Встраивание</h2><p>В целом, сценарий работы с библиотекой распознавания документов такой же, как и в <a href="https://tproger.ru/articles/kak-vstroit-raspoznavanie-pasporta-rf-v-android--powagovoe-rukovodstvo">прошлой статье</a> про распознавание паспорта: создаём движок, формируем настройки сессии, заводим саму сессию и кормим её картинками, после чего работаем с результатом распознавания. В отличие от документов, удостоверяющих личность, гибкие и жесткие формы могут быть многостраничными, с одинаковыми «по смыслу» полями на каждой странице. Помимо этого, часто документы загружают «пакетом», и в этом случае на одном изображении могут быть несколько разных документов. Поэтому результат распознавания устроен сложнее, чем в прошлом примере. Есть «результат распознавания», внутри него лежит набор найденных документов, каждый документ разбивается на «логический» и «физический» набор полей:</p><p>Логическая и физическая части документа разбираются отдельно, так как в некоторых случаях геометрия вообще не нужна (если результат распознавания документа дальше идёт в базу данных):</p><p>Как правило, результат распознавания представляют в виде json-объекта, но для наглядности лучше всего пользоваться html — особенно в случае, когда логические и физические поля имеют больше одного соответствия. Вот простенький генератор html на основе документа:</p><p>В результате получается удобная для взаимодействия html-страничка. Если добавить немного фантазии, то можно сразу сделать форму, в которой можно будет проверять и дополнять неуверенно распознанные поля — очень удобно в случае, если качество изображения плохое или документ плохо пропечатан.</p><p>Конечно, помимо андроида встроить распознавание и организовать удобные представление документа (при необходимости) можно и на любой другой платформе, однако с трендом на создание банковских офисов нового поколения и развития курьерской сети автоматизация ввода документов с помощью средненьких мобильных устройств на Андроиде становятся актуальной задачей.</p><p>На этом мы не заканчиваем, ждите новых статей!</p>]]></content:encoded>
    </item>
    <item>
      <title>CORS от А до Я: история, ошибки и грамотная настройка</title>
      <link>https://tproger.ru/articles/cors-ot-a-do-ya--istoriya--owibki-i-gramotnaya-nastrojka</link>
      <comments>https://tproger.ru/articles/cors-ot-a-do-ya--istoriya--owibki-i-gramotnaya-nastrojka?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/cors-ot-a-do-ya--istoriya--owibki-i-gramotnaya-nastrojka</guid>
      <description><![CDATA[<p>Что такое CORS, почему браузер блокирует запросы и как избежать типичных ошибок. Простое объяснение для разработчиков + рабочие решения.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/cors-ot-a-do-ya--istoriya--owibki-i-gramotnaya-nastrojka">CORS от А до Я: история, ошибки и грамотная настройка</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Браузеры]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 09 Jun 2025 11:19:14 GMT</pubDate>
      <content:encoded><![CDATA[<p>CORS (Cross-Origin Resource Sharing) — это механизм, который позволяет веб-приложениям безопасно запрашивать ресурсы с других доменов. Если вы когда-либо видели в консоли браузера ошибку вроде «No ‘Access-Control-Allow-Origin’ header», значит, вы уже столкнулись с его работой. Сегодня разберёмся, как появился CORS, зачем он нужен и как с ним работать.</p><h2>Откуда возник CORS</h2><p>Всё <a href="https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy">началось</a> в начале 2000-х, когда веб-приложения становились всё сложнее, а разработчики чаще нуждались в данных с других доменов. Однако браузеры строго придерживались политики одного источника (Same-Origin Policy, SOP), запрещавшей доступ к сторонним ресурсам. Эта политика появилась ещё в 1995 году в Netscape Navigator 2.0 и была призвана защитить пользователей от атак вроде XSS. Проблема в том, что SOP блокировала не только вредоносные, но и вполне легитимные запросы, например, к внешним API. Разработчики пытались обойти ограничения с помощью JSONP или iframe, но такие решения были небезопасными и нестабильными.</p><p>В 2005 году инженер Мэтт Ошри предложил идею более надёжного и безопасного механизма, который в дальнейшем стал основой CORS. А в 2008-м Анн ван Кестерен <a href="https://fetch.spec.whatwg.org/">подготовила</a> черновик спецификации для W3C. В 2014 году CORS официально вошёл в стандарт Fetch API и с тех пор стал ключевым элементом работы с кросс-доменными запросами. Он помогает контролировать доступ к ресурсам, сохраняя баланс между открытостью веба и безопасностью пользователей.</p><h2>Как устроен CORS</h2><p>CORS работает как диалог между браузером и сервером, основанный на HTTP-заголовках. Когда фронтенд-приложение (например, на сайте frontend.com) запрашивает данные с другого домена (api.com), браузер автоматически добавляет к запросу заголовок Origin: https://frontend.com. Этот заголовок сообщает серверу, откуда пришёл запрос.</p><p>Сервер в ответ решает, разрешать ли доступ. Если он включает в ответ заголовок Access-Control-Allow-Origin и указывает там адрес запроса (например, https://frontend.com) или символ * (разрешение для всех доменов), то браузер пропускает ответ. Если такого заголовка нет или значение не совпадает, браузер блокирует ответ — даже если сервер технически его отправил.</p><p>Для так называемых «сложных» запросов, например, с методом POST или нестандартными заголовками, браузер сначала отправляет предварительный (OPTIONS) запрос, чтобы уточнить у сервера, допустим ли основной. В ответе сервер указывает, какие методы (Access-Control-Allow-Methods) и заголовки (Access-Control-Allow-Headers) разрешены.</p><p>Пример ответа сервера с разрешением:</p><h2>Топ-7 самых частых ошибок в CORS и как их исправить</h2><p>Для того чтобы определить оптимальные методы работы, лучше учиться на ошибках. Хорошая новость в том, что почти все CORS-ошибки легко понять и исправить — если знать, где искать. Ниже собрали 7 <a href="https://arunangshudas.com/blog/7-common-cors-errors-and-how-to-fix-them/">самых распространённых ошибок</a> CORS, с которыми сталкиваются разработчики, и рассказали, как можно с ними справиться без боли и паники.</p><h3>Нет заголовка Access-Control-Allow-Origin</h3><p>Сообщение об ошибке:</p><p>"No ‘Access-Control-Allow-Origin’ header is present on the requested resource"</p><p>Когда ваш JavaScript-код пытается получить данные с другого домена, браузер ожидает, что в ответе будет заголовок Access-Control-Allow-Origin. Если его нет — браузер блокирует ответ, считая его небезопасным.</p><h4>Решение 1: Настроить сервер на разрешение CORS</h4><p>Если вы контролируете сервер, добавьте в ответ заголовок:</p><p>Access-Control-Allow-Origin: *</p><p>* — разрешение запросов с любого домена</p><p>Но для безопасности лучше указать конкретный домен:</p><p>Access-Control-Allow-Origin: http://example.com</p><h4>Решение 2: middleware CORS в Express.js</h4><p>Если вы используете Node.js с Express, добавьте<b>:</b></p><h3>Браузер блокирует preflight-запросы (OPTIONS)</h3><p>Сообщение об ошибке:</p><p>"CORS policy preflight request did not succeed" или что-то похожее.</p><p>Иногда перед настоящим запросом браузер сначала отправляет preflight-запрос методом OPTIONS, чтобы проверить, разрешен ли основной запрос. Это происходит, если:</p><ul><li>Метод запроса не GET или POST (например, PUT, DELETE).</li></ul><ul><li>В заголовках есть что-то нестандартное (например, Authorization или Content-Type: application/json).</li></ul><p>Если сервер не умеет обрабатывать OPTIONS — все ломается.</p><h4>Решение: Обработать OPTIONS-запросы на сервере</h4><p>Пример для Express.js:</p><p>app.options('*', cors()); // Разрешаем preflight-запросы</p><p>Или настройка CORS с поддержкой OPTIONS:</p><h3>Запросы с учётом сессий (credentials) блокируются</h3><p>Сообщение об ошибке:</p><p>"The value of the ‘Access-Control-Allow-Origin’ header in the response must not be '*' when the request's credentials mode is 'include'"</p><p>Вы отправляете запрос с куками или авторизацией (credentials: 'include'), но сервер разрешает доступ всем (Access-Control-Allow-Origin: *). Это запрещено — нельзя слать credentials, если разрешено всем.</p><h4>Решение: Указать конкретный домен и разрешить credentials</h4><p>На сервере также добавьте:</p><p>Access-Control-Allow-Credentials: true</p><h3>Смешанные протоколы — HTTP и HTTPS</h3><p>Сообщение об ошибке:</p><p>"Mixed content: The page at 'https://...' was loaded over HTTPS, but requested an insecure resource 'http://...'."</p><p>Ваш сайт работает по HTTPS, а API — по HTTP. Браузеры считают, что это опасно, и банально блокируют запросы.</p><h4>Решение:</h4><ul><li>Переведите API на HTTPS.</li><li>Обновите все ссылки с http:// на https://.</li></ul><ul><li>Если работаете локально и сервер только HTTP, используйте ngrok, чтобы создать HTTPS-туннель.</li></ul><h3>CORS блокирует редиректы</h3><p>Сообщение об ошибке:</p><p>"Redirect from '...api' to '...login' has been blocked by CORS policy."</p><p>API делает редирект, но в ответе на редирект нет CORS-заголовков. Браузер не знает, можно ли доверять редиректу, и блокирует.</p><h4>Решение:</h4><p>Убедитесь, что редирект тоже возвращает правильные заголовки:</p><p>Access-Control-Allow-Origin: http://example.com</p><p>Если используете fetch, добавьте:</p><h3>Неверно настроен Access-Control-Allow-Headers</h3><p>Сообщение об ошибке:</p><p>"Request header field &lt;X&gt; is not allowed by Access-Control-Allow-Headers"</p><p>Ваш запрос содержит нестандартный заголовок (например, Authorization), а сервер не пропускает его.</p><h4>Решение:</h4><p>На сервере укажите, какие заголовки разрешены:</p><h3>Метод запроса запрещён сервером</h3><p>Сообщение об ошибке:</p><p>"Method PUT is not allowed by Access-Control-Allow-Methods"</p><p>Вы отправляете запрос методом PUT, PATCH, DELETE, а сервер разрешает только GET и POST.</p><h4>Решение:</h4><p>На сервере явно разрешите нужные методы:</p>]]></content:encoded>
    </item>
    <item>
      <title>Как TanStack Query ускоряет работу с API и сокращает код</title>
      <link>https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod</link>
      <comments>https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дмитрий Скляр]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod</guid>
      <description><![CDATA[<p>Использование TanStack Query дает разработчикам возможность упростить работу с API, сократить дублирование кода и ускорить разработку. Рассказываем о проблемах, связанных с использованием API, и соответствующих решениях для повышения эффективности разработки.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-tanstack-query-uskoryaet-rabotu-s-api-i-sokrashhaet-kod">Как TanStack Query ускоряет работу с API и сокращает код</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Xen]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 28 May 2025 14:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p><i>Представьте: вы — фронтенд-разработчик, и постоянно сталкиваетесь с рядом проблем. Еще один endpoint, еще один запрос, еще десяток строк почти идентичного кода. Вручную прописываете типы, парсите ответы, обрабатываете ошибки, обновляете кэш… А через неделю бэкенд-команда меняет API — и все, что вы строили, рассыпается как карточный домик.</i></p><p><i>Так случалось, пока не появились инструменты вроде <a href="https://tanstack.com/query/latest">TanStack Query</a> и не родились «обертки», которые уменьшают объемы рутины. Как перестать тонуть в запросах на разработку API и начать дышать свободно, рассказывает Дмитрий Скляр, старший разработчик компании Axenix. </i></p><h2>API: нервная система цифрового мира</h2><p>В современном ИТ-ландшафте API давно перестал быть просто техническим термином. Теперь он — один из столпов, на котором держится цифровая цивилизация.</p><p>Философия современной разработки давно сместилась от принципа «сделай все сам» к парадигме «собери из лучших компонентов». API стал языком, на котором взаимодействуют цифровые сервисы. Он превратил ИТ-ландшафты и интернет из собрания разрозненных приложений и сайтов в единую живую экосистему, где каждый элемент может взаимодействовать с другим.</p><p>Когда разработчик использует API картографического сервиса, ему не нужно разбираться в геоданных или алгоритмах прокладки маршрутов — он просто отправляет запрос и получает готовое решение. Это похоже на то, как мы пользуемся электричеством: не нужно понимать, как работает электростанция, чтобы включить свет в комнате.</p><p>Но настоящую революцию API совершил в бизнесе, создав целые экосистемы цифровых услуг. Такие компании, как Stripe, Twilio или Plaid построили свои империи именно на API, предоставляя другим разработчикам готовые «кирпичики» для создания финансовых, коммуникационных или аналитических сервисов.</p><p>Внутри крупных компаний API выступают в роли «дипломатов» между микросервисами и ИТ-системами, позволяя разным командам работать независимо, но при этом сохранять общую согласованность. Когда маркетинговая система запрашивает данные о продажах, а сервис логистики получает информацию о новых заказах — все это происходит через четко определенные API-контракты, которые делают сложные системы управляемыми и гибкими.</p><h2>Боль, которую никто не замечает</h2><p>Но не все так просто и далеко не так радужно.</p><p>Да, формально API — это универсальный мост, например, между фронтендом и бэкендом. На практике же он часто напоминает шаткую подвесную конструкцию: данные приходят в разном формате, документация устаревает еще до релиза, а поля user_name и username существуют одновременно просто потому, что «исторически сложилось».</p><p>Типичный сценарий: вы пишете код для запроса списка товаров, все типизируя и описывая модели — 50 строк. Добавляете фильтрацию — еще 30 строк. А через месяц бэкенд меняет структуру ответа, забывает предупредить — и вы тратите день на поиск багов в трех разных местах.  И самое обидное: 80% этого кода — копипаст. Проверка ошибок, трансформация данных, инвалидация кэша — все одно и то же, но плодится как вирусы.</p><p>Сюда можно добавить также сложность поддержки — API меняется, код устаревает.</p><p>Другой момент: код для каждого API-метода имеет схожую структуру. Повторяются основные шаблоны, а различия лишь в типах данных и отдельных деталях. Чем больше API-методов, тем сложнее отслеживать изменения в коде и поддерживать его в актуальном состоянии.</p><p>В итоге — дублирование кода, которое усложняет приложение, снижает его читаемость и замедляет разработку новых функций. Документация? Либо устарела, либо ее вообще не писали.</p><h2>Спасение — в системе</h2><p>Однажды наши разработчики устали писать однотипные хуки, чинить сломанные запросы и объяснять новичкам, что где находится, почему именно так это работает и почему нельзя просто взять и использовать что-то другое. Устали писать однотипный код для каждого запроса, захотели минимизировать ошибки и ускорить разработку, а также сделать работу с API более структурированной и понятной. Также у команд назрела потребность в скорейшем включении в проект новых разработчиков. Для всех этих задач подходит одно решение: унифицированный подход к API, который также наводит порядок в данных и отчетах, упрощая аналитику и логику приложения.</p><p>Тогда и родилась идея фабрики API — слоя абстракции, который скрывает рутину. Для этого мы привлекли возможности <a href="https://tanstack.com/query/latest">TanStack Query</a>, семейство библиотек для управления состоянием данных (data fetching) в клиентских приложениях. Помимо <a href="https://tanstack.com/query/latest">TanStack Query (ранее React Query)</a>, к этому классу инструментов также относятся: RTK Query (из Redux Toolkit), Apollo Client (для GraphQL) и SWR.</p><p>Их общая философия: «делать рутину невидимой для разработчика». Рассмотрим Конкретные проблемы и их решения.</p><h3>Однотипные хуки для каждого запроса</h3><p>В большинстве проектов без дополнительной абстракции каждый хук под API-запрос пишется вручную. Меняется только URL и параметры, а структура остаётся одинаковой: queryKey, queryFn, опции запроса. Это быстро приводит к копипасту, дублированию логики и усложнению поддержки.</p><p>Например, для каждого ресурса вроде пользователей, продуктов, заказов и т.д. приходится повторять одну и ту же конструкцию. Если нужно изменить поведение запроса (например, добавить retry или staleTime), правки необходимо делать в десятках мест.</p><p><b>Решение: Универсальная обёртка над хуками TanStack Query</b></p><p>Создание единой функции-генератора для хуков позволяет избавиться от повторяющегося кода. Она принимает ключ, функцию запроса и опциональные параметры — и возвращает сразу «пачку» готовых хуков.</p><p>Такой подход:</p><ul><li>снижает количество шаблонного кода;</li><li>упрощает масштабирование;</li><li>централизует поведение всех запросов;</li><li>позволяет быстро адаптироваться к изменениям (например, добавить логирование, типизацию, трансформации и т.п.).</li></ul><h3>Хрупкость при изменении API</h3><p>В типичном приложении без централизованной трансформации мы напрямую используем ответ от бэкенда. Любое изменение формата требует правок в типах, в местах использования данных, и часто приводит к багам.</p><p><b>Решение: Централизованная трансформация данных +</b> <a href="https://github.com/typestack/class-transformer">class-transformer</a>.</p><p>С помощью <a href="https://github.com/typestack/class-transformer">class-transformer</a> можно объявить классы сущностей и задать правила преобразования один раз.</p><p>Плюсы:</p><ul><li>Все данные автоматически приходят в нужном виде;</li><li>Компоненты работают с гарантированно типизированными данными;</li><li>Один источник правды: при изменении API – правим только Entity-класс;</li><li>Удобно масштабируется, особенно если API большое и сложное.</li></ul><h3>«Мусор» в данных и отчётах</h3><p><b>Решение: Валидация данных при трансформации (например, через class-validator); автоматическая синхронизация кеша (актуальные данные во всём приложении).</b></p><p>Почему это лучше ручного подхода? Смотрите: строк кода на 1 endpoint при ручном управлении <b>потребуется 30+</b>, а <b>с фабрикой —  5-10</b>. Времени для добавления нового поля —  1<b> час вручную, с фабрикой —  5 минут</b>. Количество мест для правки при изменении API —  вручную их много, с фабрикой — лишь одно.</p><p>Реальный кейс: в проекте с 50+ endpoint’ами переход на фабрику <b>сократил код на 70%</b>. Разработчики перестают быть «переводчиками» между API и интерфейсами сервисов и приложений, а сосредотачиваются на бизнес-логике. Как сказал один тимлид: <i>«Теперь мы не фиксим баги данных, а делаем фичи, которые нравятся пользователям»</i>.</p><p>Еще пример: вместо пяти отдельных хуков для CRUD-операций фабрика дает одну функцию createApi(). Вместо ручного парсинга — автоматическую трансформацию данных через <a href="https://github.com/typestack/class-transformer">class-transformer</a>. А главное — единые правила игры для всего проекта.</p><p>Как это работает? Представьте, что вы говорите системе: «Вот endpoint для товаров, вот их модель данных, вот правила валидации» —  а все остальное она делает за вас. Хотите получить товар по ID? Пишете useGetByIDQuery. Нужно обновить? —   useUpdatetMutation. И никакого шаманства с ручным описанием каждого хука.</p><p>Но главное — когда бэкенд меняет API, правки нужны только в одном месте. А новые разработчики перестают спрашивать: <i>«Почему у нас три разных способа загрузить список пользователей?»</i>.</p><h2>Как договориться и не сойти с ума</h2><p>Проблема в том, что без четкого контракта, набора правил и подходов фронтенд- и бэкенд-разработчики живут в параллельных реальностях. Один думает, что данные придут в camelCase, другой шлет их в snake_case. Один ожидает массив, другой неожиданно подсовывает null. Итог —  бесконечные баги, исправления «на живую» и испорченные нервы.</p><p>Решение? Четкий контракт. Простой, прозрачный, однозначный. В нем должны быть:</p><ol><li>Единые правила именования — если бэкенд отдает snake_case, фронтенд не должен гадать, будут ли остальные поля в camelCase, или, например, если в одной модели данных full_name , в другой не будет fullname и так далее.</li><li>Единый формат запросов и ответов.</li><li>Договоренности о структуре URL, формате данных и кодах ошибок.</li><li>Строгая типизация — TypeScript-интерфейсы, которые знают, какие поля обязательны, а какие могут отсутствовать.</li><li>Документация, которая не врет — если Swagger говорит, что поле email есть, оно должно быть. Всегда.</li></ol><p>И самое главное — этот контракт должен соблюдаться. Если бэкендеры меняют API, они обязаны предупредить. Иначе фронтенд превращается в сапера, который каждое утро разминирует прод.</p><p>Но, как правило, контракт сделать тяжело.  Каждый видит REST по-своему: разные URL, форматы данных, обработка ошибок. Модели данных непоследовательны — поля то есть, то их нет, вложенность меняется. Документации либо нет, либо она устарела, так что API изучаем методом проб и ошибок. От такого надо отказываться сразу и стараться договорится на берегу.</p><p>Для унификации и строгого соответствия данных мы используем <a href="https://github.com/typestack/class-transformer">class-transformer</a>: автоматически приводим данные к нужным форматам, вместо работы с сырыми JSON-объектами. Так получаются экземпляры классов с методами и свойствами. Далее убираем ручную обработку и проверки данных, преобразовываем вложенные структуры и применяем кастомные трансформации.</p><h4>Что получаем в итоге?</h4><p>Когда контракт есть, а обертка API готова, магия начинает работать:</p><ul><li><b>Простота использования</b>: вместо десятков хуков — единая фабрика createApi(). Меньше boilerplate-кода.</li><li><b>Мощные возможности</b>: данные приходят уже в нужном формате без ручных проверок. Кэш, инвалидация и оптимизации —  <a href="https://tanstack.com/query/latest">TanStack Query</a> делает за вас всю грязную работу.</li><li><b>Новички влетают в проект: </b>больше не нужно объяснять, почему useGetEntity в одном компоненте работает не так, как в другом.</li><li><b>Активное сообщество: </b><a href="https://tanstack.com/query/latest">TanStack Query</a> —  тысячи разработчиков, готовых ответить на вопросы. Здесь можно найти примеры для любых кейсов: от интеграции с <a href="https://nextjs.org/">Next.js</a> до кастомного кеширования.</li><li><b>Нет ограничений по использованию: </b><a href="https://tanstack.com/query/latest">TanStack Query</a> —  не только для React, есть версии для <a href="https://tanstack.com/query/latest/docs/framework/vue">Vue</a>, <a href="https://tanstack.com/query/latest/docs/framework/svelte">Svelte</a> и даже <a href="https://tanstack.com/query/latest/docs/framework/solid">Solid.js</a>. Также работает с любым API — REST, GraphQL, WebSockets.</li><li><b>Работа с SSR без боли: </b>готовая интеграция с <a href="https://nextjs.org/">Next.js</a>, <a href="https://remix.run/">Remix</a> и другими фреймворками. При этом данные, полученные на сервере, автоматически передаются на клиент. <a href="https://tanstack.com/query/latest">TanStack Query</a> синхронизирует серверный и клиентский рендеринг.</li></ul><p>Но есть и ложка дегтя. Отладка усложняется, если что-то сломается внутри обертки — придётся копать глубже. Возникает зависимость от библиотек: <a href="https://github.com/typestack/class-transformer">class-transformer</a>, axios и сам <a href="https://tanstack.com/query/latest">TanStack Query</a> становятся обязательными.</p><p>Однако игра стоит свеч. Потому что время, сэкономленное на рутине, можно потратить на то, что действительно важно —  фичи, которые понравятся пользователям, а не бесконечные правки API-вызовов.</p><h4>Сравнительные примеры</h4><p>GET /products — Список продуктов</p><p>GET /products/:id — Один продукт по id</p><p>POST /products — Создание продукта</p><p>PUT /products/:id — Обновление продукта по id</p><p>DELETE /products/:id — Удаление продукта по id</p><h4>Как это выглядит в обертке</h4><h4>Все свойства и возвращаемые хуки и конфиги из фабрики:</h4><h4>Пример использования в компонентах:</h4><h4>Использование конфигов:</h4><h4>Для сравнения с классическим решением:</h4><figure><img src="https://media.tproger.ru/user-uploads/115291/2025-05-22/efa40892-0692-4a19-9796-8f993267a22d.png" alt="Сравнение обычного использования и фабрики" /><figcaption>Сравнение обычного использования и фабрики</figcaption></figure><h2>Когда стоит переходить на обертку?</h2><p>Не каждый проект нуждается в таком подходе к API. Если у вас два-три endpoint’а и они никогда не меняются — возможно, обертка будет избыточной. Но представьте стартап, где каждый месяц добавляются новые сущности: сначала товары, потом отзывы, потом промокоды, рекомендации, аналитика.</p><p>Вот где система раскрывается на полную! Новая сущность? Пять минут на добавление — и готовы все CRUD-операции. Изменился бэкенд? Правим в одном месте — и все работает.  Пришел новый разработчик? Он не тратит неделю на изучение особенностей API.</p><p>Правда, если бэкенд живет в мире хаотичных endpoint’ов (например, GET /fetch_items, но DELETE /removeProduct), обертка не спасет.</p><h4>Но как навести порядок?</h4><p>Главный секрет — общаться. Не ждать, пока API сломается, а сразу договориться:</p><ul><li>Какие будут названия полей (created_at vs createdAt);</li><li>Как структурированы ошибки;</li><li>Когда и как можно менять контракт.</li></ul><p>Как сказал один разработчик: «фронтенд и бэкенд — как соседи по коммуналке. Можно ругаться из-за бардака на кухне, но лучше сесть и написать правила совместного проживания».</p><p>А напоследок —  график, для закрепления разницы между работой с оберткой и без нее.</p><figure><img src="https://media.tproger.ru/user-uploads/115291/2025-05-22/5f23aca4-8353-4ff4-8923-f24685395bb2.png" alt="График сравнительного примера" /><figcaption>График сравнительного примера</figcaption></figure>]]></content:encoded>
    </item>
    <item>
      <title>Конвейер DevOps, часть 3: пайплайны и хуки в Git</title>
      <link>https://tproger.ru/articles/konvejer-devops--chast-3--pajplajny-i-huki-v-git</link>
      <comments>https://tproger.ru/articles/konvejer-devops--chast-3--pajplajny-i-huki-v-git?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Филон]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/konvejer-devops--chast-3--pajplajny-i-huki-v-git</guid>
      <description><![CDATA[<p>В этой серии статей Олег Филон, ментор Эйч Навыки, рассказывает, как прийти к крутому CI/CD пайплайну. Сегодня разбираемся, как работать с пайплайнами и хуками в Git.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/konvejer-devops--chast-3--pajplajny-i-huki-v-git">Конвейер DevOps, часть 3: пайплайны и хуки в Git</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[unix]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[GitLab]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[ICO]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 26 May 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Я — Олег Филон,<a href="https://h.careers/curators/oleg-philon"> ментор Эйч Навыки</a> и Senior DevOps Engineer. В этой статье расскажу, как организовать CI/CD пайплайн для контейнеризованного проекта с использованием утилиты make, сравню подходы для Docker и Podman, а также поделюсь хаком с использованием Git bare репозитория для автоматизации деплоя.</p><p>Первые две части лежат здесь: <a href="https://tproger.ru/articles/konvejer-devops--chast-1--kak-organizovat-rabochee-mesto-i-nastroit-oblako-iz-kvm-libvirt">рабочее место/облако</a> и <a href="https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-mise">Fedora Core/mise</a>.</p><h2>Начало проекта и утилита make</h2><p>Представим идеальную ситуацию: я не только девопс, но и проектный менеджер, выбираю архитектуру проекта, и инструменты, и команду разработчиков, то есть полностью контролирую проект. В жизни такое вряд ли встретишь, но нам это нужно для примера, чтобы рассмотреть разные варианты.</p><p>Первый — классический пайплайн — это утилита make. Обычно она используется для сборки программ из исходного кода. На самом деле make хорошо подходит для решения сразу нескольких задач.</p><ul><li>Первая задача — отслеживание зависимостей одних файлов от других, например, при изменении сервиса пересобрать только соответствующий контейнер.</li><li>Вторая задача, легко реализуемая через make — сборка в один файл много команд или скриптов, чтобы удобно их организовать. Как правило, сборка образа, его загрузка в репо, удаление временных файлов и прочее делается несколькими рутинными командами. Точно так же можно поместить в Makefile команды запуска сервисов и тестирование приложения локально.</li><li>Если эти этапы прошли успешно, можно выполнить коммит кода в репо проекта, сделать деплой в dev или stage environment. В github actions это называется jobs и steps. В make такая группа команд называется целью, она указывается параметром при вызове.</li></ul><p>Так, несмотря на разную терминологию, по сути можно создать полноценный пайплайн для современного проекта с контейнеризованными сервисами.</p><h3>Разбираемся на практике</h3><p>Возьмём для примера проект с прокси сервером traefik и бэкендом на golang из репозитария <a href="https://github.com/ophilon/awesome-pods">awesome-pods</a>. Этот репо задуман как форк замечательного проекта <a href="https://github.com/docker/awesome-compose">awesome-compose</a>, в котором собраны конфиги docker compose для 41 самого популярного сервиса. Я же пытаюсь сделать что-то похожее для манифестов podman. Приглашаю к сотрудничеству начинающих девопс — сможете поучаствовать в открытом проекте, заработать почётные гитхаб-бейджи и улучшить своё резюме. Подробнее — <a href="https://github.com/ophilon/awesome-pods/blob/main/CONTRIBUTING.md">здесь</a>.</p><p>Мой проект в интересном положении: сделаны манифесты для нескольких сервисов, опробованы описанные выше подходы для миграции конфигов compose.yaml в манифесты kube.yaml. Но захотелось большего: а почему бы не сделать сразу пайплайны для тестирования, коммита в апстрим, деплоя и прочее. Зайдём в каталог traefik-golang и создадим пару мейк-файлов. Для начала сделаем всё это локально, начнём с make_compose:</p><p>Этот файл уже в истории, равно как и соответствующий README.md, привожу его для примера. Так как я делаю конфиги сразу для двух платформ — docker и podman, для включения соответствующего Makefile’а нужно сделать линк на него: ln -s make_compose Makefile.</p><p>Отлично, основную идею обсудили, идём дальше. В docker’е есть замечательная опция context, позволяющая работать с любыми серверами, где настроен доступ. В нашем случае список контекстов выглядит так:</p><p>Здесь я использовал простейший хак — сделал копию дефолтного контекста с именем localhost. Теперь мы можем сделать наш пайплайн способным на удалённый деплой. Достаточно прописать в /etc/hosts имя и адрес нашего dev сервера. Вот новая версия make_compose:</p><p>Поясню немного подробнее.</p><ul><li>Самая первая строка — стандартное объявление списка целей.</li><li>Строки 2-4 задают дефолтное значение переменной, если оно не задано в текущем env.</li><li>В хелп — строки 5-10 — добавлено предупреждение о текущем контексте, он задаётся в глобальной переменной, например, export DKR_CONTEXT=localhost для локального контекста.</li><li>Также добавлена цель commit в репо — строки 17-21 — после выполнения цели test.</li><li>Test — строки 31-32 — в свою очередь, выполняется для текущего контекста, см. хак #1. Имя контекста должно совпадать с именем хоста нашего dev-сервера.</li><li>Добавлена также цель clean: очистка старых образов с локальном репо,и зависимости в цель up. Здесь убеждаемся, что образ пересобран и старые контейнеры остановлены.</li></ul><p>Отлично, пайплайн для докера работает. Пробуем сделать то же самое для подмана. Здесь нас ждёт сюрприз, попробую рассказать в стиле прямого репортажа. Первоначально наш пайплайн для podman выглядел вот так:</p><p>В строке 5 определяются зависимости: target back соберёт исполняемый файл только в том случае, если код main.go или сам make_pods новее уже собранного бинарника.</p><p>Строка 6 удаляет backend контейнер с едва заметным знаком минус -, чтобы игнорировать ошибку, если контейнер с именем backend не существует.</p><p>Строки 7–10 создают контейнер с именем backend из пустого (scratch) контейнера — команды buildah следуют обычным командам Dockerfile, но в нижнем регистре: FROM -&gt; from, COPY -&gt; copy, RUN -&gt; run, ENTRYPOINT -&gt; config –entrypoint и т. д. Здесь вы видите основное отличие от традиционного docker buildx подхода — вы работаете в двух контекстах одновременно: в локальном контексте, используя установленный компилятор go, и в контексте контейнера, копируя файлы в/из контейнера, запуская команды внутри контейнера и т. д. Другая новая возможность buildah — вы можете собирать образ шаг за шагом, то есть отлаживать процесс сборки.</p><p>Строка 8 компилирует main.go в исполняемый файл back с соответствующими флагами.</p><p>Строка 11 создаёт из контейнера новый образ (image) с тегом backend:latest.</p><p>Цель up — строка 16 — зависит от цели down — строка 14, — то есть она сначала останавливает pod и удаляет контейнеры, если они всё ещё запущены, затем запускает новый под.</p><p>Цель down в строке 15 подставляет глобальную переменную $XDG_RUNTIME_DIR из env пользователя в kube.yaml, используемый далее в podman kube командах, принимая новый манифест со стандартного ввода. Это также специфика podman — он работает полностью в пространстве пользователя, контейнеры взаимодействуют через собственный podman.sock. Таким образом, делаем пайплайн независимым от UID.</p><p>В подмане есть фунциональность наподобие docker context, под другим именем, в подкоманде system connection:</p><p>Первым в списке стоит настроенная в <a href="https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-misel">прошлой статье ВМ</a>. Пока искал правильные опции для создания коннекшена (aka контекст в докере), столкнулся с подсказкой от подмана — «создайте сначала машину», а именно:</p><p>Выполнил эти рекомендации, подман выкачал, настроил и добавил два новых коннекшена для новой ВМ. Какой же меня ждал сюрприз, когда я стал смотреть, что же это за machine. Во-первых, в моём HOME появились новые файлы и каталоги:</p><p>Во-вторых, это полноценная ВМ fedora coreos:</p><p>Конечно, приятно, что моё мнение совпало с мнением авторов подмана, точнее, со стратегией RedHat — fedora coreos наиболее подходящая система для контейнерных приложений. С другой стороны, ВМ в подмане крутится полностью внутри пространства пользователя. У меня уже настроена почти такая же для удалённой работы всей команды разрабов. Решено: останавливаем новую виртуалку и правим мейкфайл для подмана по образцу компоуза, делаем пайплайн для деплоя и локально, и на удалённый дев-сервер.</p><p>Но прежде нам понадобится ещё один хак #2. Если в случае докера переключение контекста можно было сделать любой переменной, то для подмана между локальным соединением через сокет и удалённым, через uri:ssh, имя переменной фиксировано <a href="https://docs.podman.io/en/stable/markdown/podman.1.html">CONTAINER_HOST</a>. Вот как выглядит пайплайн make_pods.v1, настроенный и для локальной сборки, и для деплоя в наш дев-сервер:</p><p>По большей части цели мейкфайла остались теми же, но для удалённого деплоя настраиваем переменную export CONTAINER_HOST=ssh://dev@fc42dev:22/run/user/1001/podman/podman.sock — берём её из коннекшена, она служит переключателем между локальным и удалённым контекстом. Для локального контекста эту переменную надо удалить: unset CONTAINER_HOST. Команды в строках 19, 21 и 23 — это обычные команды шелла, они также меняются на локальное либо удалённое исполнение, переопределяются на основе этой же переменной CONTAINER_HOST.</p><p>Как заметил внимательный читатель, в цели back исчезла сборка контейнера утилитой buildah. Как и для docker compose, используется возможность самого подмана создавать образы на основе Containerfile, он же Dockerfile, эти названия синонимичны. Это намёк: пора отвыкать от слова докер, контейнеры уже давно стали основой облачных вычислений, для них созданы сотни приложений, например, <a href="https://www.cncf.io/">CNCF</a> и <a href="https://adriancitu.com/2021/12/30/containers-landscape-seen-through-oci-and-cncf-standards-lens/">общепризнанные стандарты</a>.</p><h2>Принципиальный вопрос о контейнерах</h2><p>Основное их преимущество — новый способ доставки приложений в облака, решение проблем с зависимостями, версиями библиотек, фреймворков и проч. Сборка контейнеров в контейнерах — побочный эффект облачных сервисов Github, Gitlab и других, с одной стороны, и ограничения Docker — с другой. Он не умеет, в отличие от подмана, точнее, от его сопутствующей утилиты buildah, выполнять билд и создавать образ, используя локальное окружение.</p><p>Основная проблема сборки образа внутри контейнера — неэффективное использование кэша. Да, появились возможности как-то сохранять объемные загрузки внешних библиотек, модулей: это опции --mount=type=cache для <a href="https://docs.docker.com/build/cache/optimize/#use-bind-mounts">некоторых языков</a>. Но, во-первых, эти возможности используются далеко не всегда. Во-вторых, опции для кэширования отличаются в podman и buildah, см. podman-build(1), придётся делать отдельный Containerfile. В-третьих, эффект от такого кэширования минимален. Предлагаю замерить время сборки, сделав ещё одну, третью версию пайплайна. Сначала соберём команды для buildah в отдельный файл:</p><p>и поправим пару строк в пайплайне:</p><p>Уточню условия нашего эксперимента — мы настроили одинаковую среду разработки с помощью утилиты mise (<a href="https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-mise">предыдущая статья</a>) на нашем дев-сервере и у каждого из разрабов команды. Репозитарий git использует этот же дев-сервер, доступ к репо и серверу по ключу, парольный доступ закрыт. Пайплайны настроены как для локальной сборки, так и на дев-сервере. Перед запуском 3-й версии пайплайна на дев-сервере нужно сделать коммит изменений в репо — buildah не знает о коннекшенах, работает с кодом в текущем каталоге (строка 1): после логина на сервер переключается в корень проекта. Предварительно выкачиваем образ компилятора go для сборки в контейнере — это вполне честно, мы же выкачали и настроили компилятор golang заранее. Замеряем:</p><p>Мы получили 10+-кратный выигрыш по времени сборки образа для подмана. Абсолютные времена не важны, также не влияет, запускали мы сборку локально или на дев-сервере — мы сравниваем только билд в контейнере и в настроенном локальном окружении. Третье измеренное время — сборка в Docker. Он умеет собирать только в контейнере, для него настроили кэширование в Containerfile:</p><p>Но оно не сильно помогло. Конечно, наш проект игрушечный, golang кэширует лучше других языков, но в целом вывод понятен: сборка в контейнере далеко не оптимальный вариант, если есть возможность настроить дев-сервер для работы команды.</p><p>Ещё замечание: конечно, образы, собираемые buildah, совместимы с Docker, их можно использовать в конфигах compose.yaml. Но для этого надо настроить репозиторий образов и сначала загрузить образ в него. Локальные репозитории отличаются: Docker использует общий репо для всех пользователей — Docker Root Dir: /var/lib/docker, а в подмане всё хранится в домашнем каталоге пользователя — graphRoot: /home/$USER/.local/share/containers/storage.</p><p>Как я предположил в самом начале, мы попробовали вариант с гипотетической идеальной командой разрабов, работающей в Линукс и умеющей в make. А как быть обычному девопсу с разношерстой командой, где кто-то сидит на Винде, а кто-то ни за что не откажется от привычного Макбука на M4? Есть вариант и для этого случая. Пусть они пишут код и тестируют его как им нравится, а в нашем репо на дев-сервере мы сделаем хак #3, а именно git hook и bare репозиторий — githooks(5), выполняющий наши цели сборки и старта приложения при коммите в репо.</p><p>Для этого на пару минут придётся стать безжалостным хакером, удаляющим лишнее и открывающим скрытые возможности гита. Выполняем следующие шаги:</p><ol><li>Заходим под юзером dev на сервер, создадим пустой каталог, например, mkdir -pv ~/bare/t0. Это станет новым GIT_DIR, зайдём в него и выполним cd ~/bare/t0;git init --bare.</li><li>Видим, что файлы, обычно спрятанные в каталоге .git, лежат прямо в корне. Сделаем дополнительно каталог для логов mkdir logs. Переходим в каталог hooks и создаём файл, где укажем команды выполнения при каждом изменении в репо.</li></ol><p>Закомментированные строки 3, 8, 9 полезны при отладке пайплайна. Строки 4 и 5 задают, что есть, собственно, репозиторий, переменная GIT_DIR и переменная WORK_TREE (куда будут записываться файлы проекта). В цикле от строки 6 до 14 читаются и обрабатываются три переменные, с которыми гит вызывает этот хук. Строка 11 принимает все изменения в репо и обновляет WORK_TREE — всё то, что гит обычно делает в общем каталоге, как видим, в bare репо они разные. Далее, в 12 строим имя лога и строка 13 — собственно, пайплайн.</p><ol><li>Идём в каталог, где расположен репо проекта. Без страха и сожаления удаляем старый и создаём новый под тем же именем: cd ~/src;rm -rf traefik-golang;mkdir traefik-golang.</li><li>Завершаем сессию на дев-сервере, возвращаемся на рабочий комп и заходим в репо проекта. Конечно, репо цел, клоны репо не так просто уничтожить, пока есть хотя бы одна копия. Теперь смотрим старые настройки git remote -v и удаляем их git remote remove fc42dev в моём случае. Создаём новый remote, указывая новый гит bare репо: git remote add bare.t0 dev@fc42dev:~/bare/t0. Это также нужно сделать всем разрабам в их локальных копиях.</li><li>Проверяем результат. Возможно, нужно сделать новый комит и push в новый remote. Стоит посмотреть подробнее, как изменился репо проекта на сервере: проверить логи в ~/bare/t0/logs, сравнить конфиги обычного репо проекта и на сервере, проверить, какие команды перестали работать в серверном репо. Например, в WORK_TREE не работают команды гит status; branch; commit; log. То есть наш хак #3 с git --bare не только позволил делать деплой на сервере, но также защитил репо от локальных изменений, а серверный репо всегда в чистоте и порядке. Можно редактировать код, но закомитить его только через обычный репо. Изменения на сервере удалятся после любого коммита.</li></ol><p>Надеюсь, мне удалось показать, что пайплайны можно делать на основе древней забытой утилиты make. В следующей статье разберём, как можно добавить в наш скромный дев-сервер нечто похожее на монстров гит-сервисов, Gitlab и Github, создавать пайплайны, совместимые с github Actions, предоставить команде разрабов привычный интерфейс репо в браузере.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как я пытался допилить криптобота, но всё пошло не по плану</title>
      <link>https://tproger.ru/articles/zapilil-alerty-i-obaldel</link>
      <comments>https://tproger.ru/articles/zapilil-alerty-i-obaldel?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Robin Gad]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/zapilil-alerty-i-obaldel</guid>
      <description><![CDATA[<p>История о том, как простой криптобот превратился в боль: алерты, WebSocket, systemd, конфликты процессов и неожиданные баги на сервере.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/zapilil-alerty-i-obaldel">Как я пытался допилить криптобота, но всё пошло не по плану</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Telegram]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Криптовалюты]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 25 May 2025 09:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Изначально всё выглядело просто: <a href="https://t.me/Kriptoprice_bbot">криптоконвертер</a>, немного кеша в JSON и WebSocket от Binance для алертов. Но с каждой новой фичей бот начинал вести себя странно. Настройки не сохранялись, алерты спамили, структура данных ломалась, сервер выдавал ошибки, а старый скрипт — не сдавался. В этой истории — реальный опыт разработки и отладки телеграм-бота, в котором всё пошло не так, как планировалось.</p><h2>Начало истории</h2><p>Это был просто <a href="https://t.me/Kriptoprice_bbot">криптоконвертер</a>, который работал так:</p><p>REST API CoinGecko — раз в 10 минут обновлял курс. Кэш в JSON — чтобы не дудосить API. Теперь я решил добавить алерты.</p><p>Так как мой мой бот в дальнейшем будет обрастать всё новыми и новыми функциями, я решил разделять их на отдельные файлы.</p><p>Чтобы не приходилось постоянно отправлять запросы, реализовал алерты через Websocket binance.</p><p>Сделал сохранение настроек в database.json.</p><p><i>Я реально думал что это будет просто, ошибся:</i></p><ul><li>При деплое выдало Sintaxis Error , хотя на компе всё было нормально;</li></ul><ul><li>Сохранение алертов пропадало после конвертации;</li></ul><ul><li>Алерты дико спамили.</li></ul><p>Когда я запускал бота с компа, никакой синтаксической ошибки не наблюдалось, но после того, как закинул бота на сервер, мне выдало ошибку в строке:</p><p>Сначала думал, что дело в кодировке и стоит по дефолту UTF-8 BOM. Но нет. Решил: сервер не видит, что скобка открытая в этой строке закрывается ниже. Разделил код:</p><p>Ошибка исчезла. Запустил, казалось бы, всё работает. Ставлю галочки на крипте, выхожу в меню, захожу обратно в алерты — галочки на месте. Но стоит мне перейти в конвертер и вернутся обратно, как галок и след простыл.</p><figure><img src="https://media.tproger.ru/user-uploads/109988/2025-05-09/5ba237f7-2282-4ebc-82fd-f44a071ff11e.png" alt="" /></figure><p>Долго копался в коде, пока не понял, что сама структура данных была кривой:</p><p>Datetime ломал JSON — нельзя просто так сериализовать datetime.now().</p><p>Разделил струтктуру сохранения, чтобы конвертация не влияла на алерты:</p><p>И добавил преобразование datetime  в str</p><p>Закинул на сервер. При запуске бот не заработал, поэтому я включил режим отладки и мне выдало такое:</p><p>Старый скрпит не хочет уступать без боя. Это конфликт ботов.</p><p>Дело в том, что я запускаю сервисы через systemd и всегда настраиваю там автозапуск на случай падения бота. Но иногда это выходит боком. Приходится включать множество команд по убийству процесса  по типу:</p><p>sudo pkill -f “python.*bot.py” &amp;&amp; sleep 2</p><p>kill -9 &lt;PID&gt;</p><p>pkill -f bot.py</p><p>Но старый бот так и не умирает. А через некоторое время сам по себе перестает работать. Я пока не понял эту аномалию. Возможно, после команды sudo systemctl reload надо просто попить чаю  и дать время сервису нормально перезагрузиться.</p><p>Хорошо, бота мы убили. Нового запустили. Все кнопки работают. Включили алерты на ETH и стали ждать…</p><p>И тут началось…</p><figure><img src="https://media.tproger.ru/user-uploads/109988/2025-05-09/caab09fc-fc3e-4454-8a34-f81c4cfa3840.png" alt="" /></figure><p>Когда Эфириум  подскачил на 7%, бот сошёл с ума, начал материться и  спамить при малейшем сдвиге курса.</p><p>Было решено  игнорировать мелкие колебания курса, установить минимальный порог реагирования 5% и высылать алерт не чаще чем 5 минут от предыдущего:</p><p>В итоге мой крипобот заработал, но я уверен: при тестировании вы обнаружите еще кучу багов. Далее хочу добавить кнопки для самостоятельного выбора порогов алерта и графики курса.</p><p>Со скриптом можете ознакомится <a href="https://gitflic.ru/project/system_develop/kripto_bot">здесь</a>.</p><p><i>P.S. Пишите комментарии, предлагайте свои идеи. Деконструктивная и агрессивная критика приветствуется!))</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI?</title>
      <link>https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-</link>
      <comments>https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Державин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-</guid>
      <description><![CDATA[<p>Узнайте, как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI: генерация запросов, типизация и меньше ручной работы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-">Как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI?</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 23 May 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ручная интеграция API и веб-приложения часто приводит к проблемам с обратной совместимостью контрактов. На стороне фронтенда это приводит к падению приложения, на бекенде — к потере данных с фронтенда. Один из способов предотвратить такие сложности — кодген.</p><p>В статье мы разберёмся, как запустить кодген на основе OpenApi схемы для веб-приложения на стэке React + redux-toolkit.</p><p>Для справки: контракт — соглашение о формате данных между клиентом и сервером</p><h2>Какие проблемы решает кодген?</h2><p>Любой разработчик может совершить ошибку: неудачно разрезолвить мердж-конфликт, забыть предупредить о рефакторинге или банально опечататься.</p><p>В микро-сервисной архитектуре цена таких ошибок высока — оперативно доставить обновления многочисленным клиентам иногда бывает проблематично.</p><p><i>Кодген снижает вероятность таких ошибок, так как методы статично создаются на основе схемы, которая, в идеале, также должна генерироваться на основе кода бекенда. Рассмотрим подробнее.</i></p><h3>Синхронизация контрактов</h3><p>При активной разработке бекенд постоянно обновляет контракты. Без кодгена все обновления на стороне фронтенда синхронизируются вручную.</p><p>Также на основе контрактов создаются побочные интерфейсы, например, для redux-слайсов и react-компонентов. Ошибка в основном контракте создает цепочку проблем со связанными типами и интерфейсами.</p><p>Сочетание кодген-контрактов и typescript-утилит для создания побочных интерфейсов делает доставку обновлений простой и безопасной.</p><h3>Устранение дубликатов</h3><p>Когда проект не имеет четкой структуры, контракты оказываются разбросаны по всей кодовой базе. Разработчики, не сумев найти нужный интерфейс, просто создают свои варианты контрактов. Так рождаются дубликаты и несогласованность.</p><p><i>Кодген решает эти проблемы, создавая единую точку входа для всех методов и интерфейсов.</i></p><p><b>Совет:</b> Кодген проще и безопасней внедрять на начальных этапах разработки. А в уже написанных проектах — стоит внедрять его постепенно, например, по несколько эндпойнтов за раз.</p><h2>Что понадобиться для кодгена?</h2><p>Прежде чем запускать кодген нам нужно минимально настроить проект:</p><h3>Подготовить схему</h3><p>OpenAPI-схема в формате yaml:</p><p><b>Важно: </b>Любая ошибка в схеме приводит к поломке кодгена. Поэтому важно, чтобы ваша схема проходила валидацию.</p><p>Проверить валидность OpenAPI схемы можно на в <a href="https://editor.swagger.io">swagger редакторе</a></p><h3>Настроить Redux</h3><p>Redux-клиент для API-запросов:</p><p>Redux-слайс для хранения данных из API-запроса:</p><p>Redux-хранилище с подключенным API-клиентом и редюсером:</p><h3>Настроить кодген</h3><p>Для кодгена возьмём официальную библиотеку от Redux — <a href="https://www.npmjs.com/package/@rtk-query/codegen-openapi">@rtk-query/codegen-openapi</a></p><p><a href="https://www.npmjs.com/package/@rtk-query/codegen-openapi"></a>Затем создадим файл настройками кодгена:</p><p>Библиотека хороша тем что покрывает все основные потребности, позволяя быстро запустить когден без долгих настроек. Цена удобства — отсутствие гибкости. Адаптировать результат когдена под сложный кодстайл будет проблематично.</p><p>Если вам нужна гибкость, попробуйте использовать <a href="https://www.npmjs.com/package/@openapitools/openapi-generator-cli">@openapitools/openapi-generator-cli</a> с различными готовыми шаблонами или <a href="https://github.com/orval-labs/orval">Orval</a> от tanstack</p><h2>Запуск кодгена</h2><p>Для запуска добавим команду в package.json:</p><p>Далее заходим в корень проекта и запускаем команду:</p><p>На выходе должен получиться файл codegenApi.ts с содержимым:</p><p>Проверяем результат:</p><ul><li>Инджект кодген эндпойтов в baseApi;</li><li>Системные типы Props Response Error;</li><li>Контракт User;</li><li>RTK-query хуки.</li></ul><p><b>Совет: </b>создание кодгена можно автоматизировать, например, сделать его отдельным этапом в CI/CD, который выполняется прямо перед билдом приложения.</p><h2>Возможные проблемы</h2><h3>Необычные типы данных</h3><p>При внедрении кодгена в проект на Kotlin я столкнулся с проблемой парсинга: кодген не мог распарсить тип данных LocalDateTime, который должен возвращать строку с датой в формате ISO. Вместо строки когден возвращал пустой объект.</p><p>LocalDateTime — это класс из Java-библиотеки java.time, который представляет дату и время без учёта временной зоны.</p><p>Данную проблему я решил с помощью кодмода:</p><p>Кодмод запускается после команды codegen. Он бежит по файлу сверху вниз, находит нужный тип и меняет его значение на string.</p><p><b>Совет:</b> При добавлении кодмода в общий файл обязательно опишите проблему которую он решает. Когда кодмодов станет много и они начнут конфликтовать, вам будет проще разобраться в их работе.</p><p>Кодмоды отлично генерируются с помощью AI.</p><h2>Плюсы и минусы кодгена</h2><p>Плюсы:</p><ul><li>Генерируем API-слой всего приложения за секунды;</li><li>Получаем автоматическое строгое создание интерфейсов и типов;</li><li>Избавляемся от опечаток в URL и параметрах;</li><li>Обнаруживаем проблемы с обратной совместимостью контрактов на ранних этапах сборки;</li><li>Экономим время.</li></ul><p>Минусы:</p><ul><li>Качество кодгена зависит от качества описания OpenAPI схемы;</li><li>Если схема не генерируется в автоматическом режиме, актуальности OpenAPI схемы потребуется поддерживать вручную;</li><li>Логику нестандартных запросов придётся описывать вручную.</li></ul><p><b>Напоминание:</b> API-слой сложного проекта невозможно покрыть кодогенерацией на 100% — это нормально. Будьте готовы при помощи методов enhanceEndpoints и injectEndpoints комбинировать когден-эндпойнты с эндпойнтам написанными вручную</p><h2>Заключение</h2><p>Кодген ускоряет разработку и делает доставку изменений с бэкенда более безопасной, однако подход требует качественной спецификации, поэтому если у вас небольшой проект — скорее всего кодген для вас будет лишним усложнением.</p><p>В остальных случаях, используя кодген, вы получите предсказуемый и масштабируемый API-слой, который сэкономит сотни часов разработки и поможет вашей команде сместить фокус c рутинных задач на создание качественной бизнес-логики для вашего веб-приложения.</p><p>Ускоряй работу, автоматизируй рутину, используй лучшие тулзы. Все самые полезные инструменты <a href="https://t.me/+iKEwDxvulHFkZDhi">тут</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как не сломать прод? Топ 5 самых частых ошибок при деплое</title>
      <link>https://tproger.ru/articles/kak-ne-slomat-prod--top-5-samyh-chastyh-owibok-pri-deploe</link>
      <comments>https://tproger.ru/articles/kak-ne-slomat-prod--top-5-samyh-chastyh-owibok-pri-deploe?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Анна Ельцова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ne-slomat-prod--top-5-samyh-chastyh-owibok-pri-deploe</guid>
      <description><![CDATA[<p>Вы все сделали идеально, нажимаете кнопку Deploy, и наступает тот самый момент, когда сердце замирает. Прод горит, мониторинги упали, команда в ужасе. Что нужно сделать, чтобы такого не было — рассказываем в статье.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ne-slomat-prod--top-5-samyh-chastyh-owibok-pri-deploe">Как не сломать прод? Топ 5 самых частых ошибок при деплое</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[GitLab]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Взлом]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Jenkins]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 23 May 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда вы деплоите, вы не просто заливаете код. Считайте, что это босс на последнем уровне, а значит — привет, ловушки и подводные камни. Ошибки на этом этапе могут стоить дорого: от недовольства техлида до потери клиентов. Мы собрали топ самых частых (и самых болезненных) багов при выкладке — рассказываем, как их избежать.</p><h2>Неправильная настройка инфраструктуры в CI/CD пайплайне</h2><p>Это одна из самых коварных и частых ошибок при деплое, особенно в сложных системах, таких как Kubernetes-кластеры, облака или гибридные инфраструктуры. Эта проблема возникает, когда шаги деплоя в пайплайне не учитывают специфику целевого окружения. Все это может вылиться в непредсказуемое поведение приложения и структуры в целом. Разбираемся, как с этим бороться.</p><h3>Настройте окружение</h3><p>Разные окружения (dev, staging, prod) часто имеют отличия в конфигурации (например, версии библиотек, лимиты ресурсов, настройки сетей). Например, переменные окружения, заданные для staging, перезаписываются в prod — зависимости ломаются.</p><ul><li>Используйте Infrastructure as Code (IaC) инструменты, такие как Terraform или Pulumi, для создания идентичных окружений.</li><li>Храните конфигурации окружений в репозитории (например, в формате YAML или JSON) и применяйте их через CI/CD.</li><li>Настройте переменные окружения через секреты (например, HashiCorp Vault, AWS Secrets Manager) и убедитесь, что они не перезаписываются случайно.</li></ul><h3>Разворачивайте по стратегии</h3><p>В Kubernetes, например, при неверной конфигурации стратегии возможны простои. Pods могут быть удалены до того, как новые успеют стартовать, или новые версии вообще не будут работать.</p><ul><li>В Kubernetes используйте RollingUpdate с настройками maxSurge и maxUnavailable, чтобы новые поды стартовали постепенно, а старые были постоянно доступны.</li><li>Настройте readinessProbe и livenessProbe, чтобы Kubernetes не направлял трафик на неготовые поды.</li><li>Ответственно подходите к настройке стратегии и выбору количества реплик.</li><li>Для Helm-чартов фиксируйте версии (helm dependency update, helm package) и используйте helm upgrade --atomic для автоматического отката при сбое.</li></ul><p>Например, в манифесте Deployment можно указать:</p><h3>Избегайте race conditions</h3><p>Параллельные процессы в CI/CD (например, одновременная сборка и деплой) могут вызывать состояния гонки.</p><ul><li>Настройте блокировки (locks) в CI/CD, чтобы не было параллельных деплоев в одно окружение (например, через environments в GitLab CI).</li><li>Используйте атомарные операции в Helm и Server Side Apply в kubectl.</li></ul><h3>Обрабатывайте ошибки</h3><p>Часто может быть такое, что нет нормальной обработки ошибок (логов, статусов). Например, доступ к сервису пропадает, но пайплайн все равно успешно завершается.</p><ul><li>Регулярно тестируйте пайплайн на staging-окружении, симулируйте реальные сценарии деплоя.</li><li>Проверяйте доступность сервисов после деплоя с помощью health-check скриптов.</li></ul><p>Новую проверку можно, например, добавить так:</p><p>curl --fail http://any-app.example.com/health</p><h2>Нет изоляции переменных окружения и секретов</h2><p>Представьте: в staging-окружении используются тестовые ключи, а в продакшене — боевые. Но из-за ошибки в CI/CD пайплайне или конфигурации staging берет продовые credentials. Как итог — тестовое удаление данных в песочнице стирает боевую базу. Или еще хуже: токены утекают из-за слабых прав доступа к Secret Manager, и вас могут спокойно взломать. Ниже рассказываем, как это пофиксить.</p><h2>Разделяйте секреты по окружениям</h2><p>Если переменные окружения или ключи не разделены между dev, staging и prod, они могут быть случайно использованы в неправильном контексте.</p><ul><li>Храните секреты в Secret Manager (HashiCorp Vault, AWS Secrets Manager, Azure Key Vault, Kubernetes Secrets) с четким разделением по окружениям (например, пути secrets/staging/db, secrets/prod/db).</li><li>Используйте префиксы или теги для идентификации окружения (например, STAGING_API_KEY, PROD_API_KEY).</li><li>Настройте права доступа CD так, чтобы пайплайн мог подтягивать только секреты, которые относятся к текущему окружению.</li></ul><p>Например, в Vault это настраивается так:</p><h2>Не храните секреты в коде</h2><p>Иначе утечка неизбежна.</p><ul><li>Уберите секреты из репозиториев и .env-файлов и используйте Secret Manager.</li><li>Для Kubernetes используйте Secret-объекты или интеграцию с внешними менеджерами (например, External Secrets Operator).</li><li>Проверяйте репозитории на утечки с помощью инструментов типа truffleHog или gitleaks.</li></ul><p>Вот пример Kubernetes Secret:</p><h2>Ограничивайте доступ</h2><p>Это принцип Scoped Permissions — с помощью него можно снизить риски случайного или намеренного использования секретов.</p><ul><li>Используйте IAM-роли в облаке (например, AWS IAM Roles for Service Accounts) с минимальными правами.<br /></li><li>Ограничивайте доступ разработчиков к продовым секретам через RBAC или Vault-профили.</li></ul><p>Вот AWS IAM-политика для staging:</p><h2>Ротируйте ключи</h2><p>Это поможет избежать ситуации, когда после инцидента или утечки ключи не обновляются.</p><ul><li>Настройте автоматическую ротацию ключей в Secret Manager (например, AWS Secrets Manager поддерживает ротацию через Lambda).</li><li>После инцидента сразу же ротируйте скомпрометированные ключи и пересоздавайте секреты.</li><li>Логируйте доступ к секретам для аудита (например, через Vault Audit Logs).</li></ul><h2>Неправильная настройка health-checks в Kubernetes или других оркестраторах</h2><p>Неправильная настройка readinessProbe и livenessProbe в Kubernetes — это, можно сказать, классика. Вы обновляете сервис, поды запускаются, но сразу же помечаются как unhealthy. Причина — неправильный readinessProbe или livenessProbe. Например, путь /healthz больше не существует, или проверка уходит в таймаут из-за долгой инициализации. В результате: контейнеры бесконечно рестартуются, сервис недоступен, кластер в панике.</p><h3>Разделяйте назначение проб</h3><p>readinessProbe проверяет, готов ли под принимать трафик, а livenessProbe — не завис ли он. Если их смешать, можно ждать сбой,  например, трафик пойдет на не до конца инициализированный сервис.</p><ul><li>Используйте разные endpoints для проб. Например, /health для readinessProbe (готовность сервиса) и /alive для livenessProbe (проверка зависаний).</li><li>Настройте readinessProbe так, чтобы она возвращала 200 только после полной инициализации (например, подключения к базе).</li><li>Для livenessProbe проверяйте минимальную работоспособность (например, ответ сервера без проверки внешних зависимостей).</li></ul><h3>Учитывай время инициализации</h3><p>Сервис может запускаться медленно, и слишком строгие таймауты приведут к сбоям.</p><ul><li>Установите initialDelaySeconds с запасом, чтобы учитывать время прогрева (например, загрузку кэша или подключение к базе).</li><li>Настройте timeoutSeconds и periodSeconds так, чтобы проба не завершалась слишком быстро, но и не крутилась вечно.</li><li>Используйте failureThreshold для нескольких попыток перед пометкой пода как unhealthy.</li></ul><p>Вот пример для сервиса с долгим стартом:</p><h3>Добавьте grace period</h3><p>Он дает сервису время корректно завершиться перед рестартом.</p><ul><li>Установите terminationGracePeriodSeconds в манифесте Deployment, чтобы под мог завершить запросы перед остановкой.</li><li>Настройте preStop хук, если нужно выполнить действия перед завершением.</li></ul><h3>Тестируйте на staging</h3><p>Проблемы с пробами часто всплывают только в проде, если staging не идентичен.</p><ul><li>Убедитесь, что staging-окружение повторяет прод по конфигурации и нагрузке.</li><li>Добавьте автоматические тесты в CI/CD для проверки endpoints (/health, /alive) перед деплоем.</li><li>Симулируйте реальные сценарии (например, медленный старт или сбой зависимостей) на staging.</li></ul><p>В CI/CD можно добавить:</p><h2>Неправильная работа с конфигурациями через Helm или Kustomize</h2><p>Представьте: обновили Helm-чарт, но в values.yaml остались старые переменные, которые ломают новые настройки. Или Kustomize патчит не тот ресурс, и манифесты применяются с ошибками. В итоге: поды падают, сервисы недоступны, и никто не знает что делать. Рассказываем, что с этим делать.</p><h3>Валидируйте Helm-чарты перед деплоем</h3><p>Так можно найти ошибки до применения манифестов.</p><ul><li>Используйте helm template или helm install –dry-run для рендеринга манифестов и их проверки.</li><li>Включите schema validation для values.yaml с помощью JSON Schema (поддерживается Helm v3.6+).</li><li>Проверяйте манифесты через kubeval или kubectl apply –dry-run=server для подтверждения соответствия Kubernetes API.</li></ul><h3>Тестируйте Kustomize-конфигурации</h3><p>Kustomize может патчить не то, что вы ожидали, если селекторы или структура неправильные.</p><ul><li>Прогоняйте kustomize build для генерации итоговых манифестов и проверяйте их перед деплоем.</li><li>Используйте kubectl apply –dry-run=server -k . для валидации в кластере.</li><li>Проверяйте селекторы патчей в kustomization.yaml на точность (например, name и namespace).</li></ul><h3>Управляйте версиями и структурой</h3><p>Несогласованность версий чартов или манифестов приводит к неожиданным изменениям.</p><ul><li>Фиксируйте версии Helm-чартов в Chart.yaml и используйте точные теги (например, 1.2.3, а не latest).</li><li>Храните values.yaml отдельно для каждого окружения (values-staging.yaml, values-prod.yaml).</li><li>Для Kustomize используйте базовые манифесты и патчи, разделённые по окружениям (например, overlays/staging, overlays/prod).</li></ul><h3>Документируйте и мониторьте</h3><p>Без документации сложно понять, что изменилось, а без мониторинга — почему упало.</p><ul><li>Ведите CHANGELOG.md для Helm-чартов и Kustomize патчей, описывая изменения в структуре и значениях.</li><li>Логируйте команды деплоя (helm upgrade –debug, kubectl apply -k .) для отладки.</li><li>Настройте мониторинг статуса подов через Prometheus, чтобы сразу видеть сбои из-за ошибок конфигурации.</li></ul><p>Вот пример получения подробного лога helm:</p><h2>Нет политики управления версиями артефактов</h2><p>С этой штукой шутить нельзя. Каждый новый билд заливается с тегом latest, и через неделю никто не помнит, какая именно версия работает в проде. А если что-то сломалось, откатиться просто невозможно: старый образ либо затерт в registry, либо его никто не пометил. На выходе — паника и хаос.</p><h3>Используйте семантическое версионирование (semver) или уникальные теги</h3><p>Так банально будет однозначность и отслеживаемость версий.</p><ul><li>Присваивайте образам теги по схеме semver (1.2.3), commit hash (abc1234) или временной метке (20250429-1345).</li><li>Избегайте latest в продакшене — это бомба замедленного действия.</li><li>В CI/CD автоматически генерируйте теги на основе версии приложения или Git commit.</li></ul><p>Вот пример тег-образа:</p><p>docker build -t my-app:1.2.3 -t my-app:$(git rev-parse –short HEAD)</p><h3>Фиксируйте версии в манифестах и пайплайнах</h3><p>Immutable теги гарантируют, что деплой всегда использует ожидаемую версию.</p><ul><li>В Kubernetes манифестах указывайте точные теги вместо latest.</li><li>Настройте CI/CD так, чтобы тег образа передавался в Helm или Kustomize как параметр.</li><li>Используйте инструменты вроде helm upgrade с фиксированными версиями чартов.</li></ul><h3>Настройте retention policy для артефактов</h3><p>Хранение старых образов позволяет откатиться к стабильной версии.</p><ul><li>В container registry (Docker Hub, AWS ECR, Harbor) настройте правила хранения, чтобы сохранять последние N версий или образы за последние X дней.</li><li>Регулярно очищайте устаревшие артефакты, но сохраняйте критические версии (например, те, что в проде).</li><li>Используйте теги для маркировки стабильных версий (например, prod-1.2.3).</li></ul><p>Пример — AWS ECR lifecycle policу:</p><h3>Автоматизируйте версионирование в CI/CD</h3><ul><li>В CI/CD пайплайне генерируйте теги на основе Git тегов, commit hash или переменных окружения.</li><li>Проверяйте, что образ с нужным тегом пушится в registry и используется в деплое.</li><li>Добавьте шаг валидации манифестов, чтобы убедиться, что теги фиксированы.</li></ul><p>Ошибки при деплое могут вылиться в серьезные проблемы для проекта. DevOps-инженеры не просто запускают пайплайны, а следят за жизненным циклом продукта — от инфраструктуры до мониторинга. Документируйте ошибки, создавайте чек-листы, автоматизируйте каждый шаг и учитесь на инцидентах. И главное — никогда не деплойте в пятницу вечером.</p>]]></content:encoded>
    </item>
    <item>
      <title>OpenJDK добавит нативный JSON API для Java — первые подробности</title>
      <link>https://tproger.ru/news/openjdk-dobavit-nativnyj-json-api-dlya-java---pervye-podrobnosti</link>
      <comments>https://tproger.ru/news/openjdk-dobavit-nativnyj-json-api-dlya-java---pervye-podrobnosti?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/openjdk-dobavit-nativnyj-json-api-dlya-java---pervye-podrobnosti</guid>
      <description><![CDATA[<p>OpenJDK добавит нативный JSON API для Java — встроенная поддержка JSON упростит парсинг, обработку и создание данных без внешних библиотек</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/openjdk-dobavit-nativnyj-json-api-dlya-java---pervye-podrobnosti">OpenJDK добавит нативный JSON API для Java — первые подробности</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 16 May 2025 09:25:45 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчики OpenJDK <a href="https://mail.openjdk.org/pipermail/core-libs-dev/2025-May/145905.html">анонсировали</a> планы по созданию встроенного JSON API для Java. Этот шаг должен упростить работу с JSON-документами, не требуя установки сторонних библиотек.</p><p>Больше новостей — в нашем тг-канале «<a href="https://t.me/your_tech">Представляешь»</a></p><p>Новый API станет частью стандартной библиотеки Java и будет следовать принципу «батарейки в комплекте», который предполагает, что базовые функции должны быть доступны без внешних зависимостей.</p><h2>Основные особенности</h2><h2>Простой интерфейс</h2><p>Новый API будет включать минимальный набор классов для работы с JSON, включая:</p><ul><li><b>JsonValue</b> — базовый интерфейс для всех JSON-значений;</li><li><b>JsonObject</b> — коллекция ключ-значение;</li><li><b>JsonArray</b> — упорядоченный список значений;</li><li><b>JsonString</b> — строковое значение;</li><li><b>JsonNumber</b> — числовое значение;</li><li><b>JsonBoolean</b> — логическое значение;</li><li><b>JsonNull</b> — пустое значение.</li></ul><p>Вот как это будет выглядеть:</p><h2>Пример использования</h2><p>API будет поддерживать базовые операции, такие как разбор строк JSON и преобразование значений:</p><p>Со временем, по мере добавления новых возможностей для сопоставления паттернов (pattern matching) в Java, этот код станет еще более лаконичным.</p><h2>Работа с числами</h2><p>Поскольку JSON не различает целые и десятичные числа, новый API будет предлагать несколько способов работы с числами:</p><ol><li>Получение строкового представления числа.</li><li>Преобразование строки в BigDecimal для точной работы с десятичными числами.</li><li>Преобразование в стандартные числовые типы Java (Long, Double, BigInteger) с автоматическим выбором подходящего типа.</li></ol><h2>Совместимость и производительность</h2><p>Первая версия прототипа уже доступна в sandbox-репозитории OpenJDK.</p><p>Несмотря на раннюю стадию разработки, она успешно прошла большинство тестов из JSONTestSuite, за исключением случаев с дублирующимися ключами, которые намеренно запрещены для упрощения обработки JSON-объектов.</p><h2>Дальнейшие шаги</h2><p>Разработчики планируют оформить предложение по улучшению языка (JEP) для включения нового API в будущие версии JDK.</p><p>Это может быть обновление существующего JEP 198 (Light-Weight JSON API) или полностью новый документ.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как работает Sharding в базах данных?</title>
      <link>https://tproger.ru/articles/kak-rabotaet-sharding-v-bazah-dannyh-</link>
      <comments>https://tproger.ru/articles/kak-rabotaet-sharding-v-bazah-dannyh-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Владислав Устинов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-rabotaet-sharding-v-bazah-dannyh-</guid>
      <description><![CDATA[<p>Что такое Sharding. Показываем, как работает шардинг в базах данных. Рассматриваем пошаговую инструкцию и основные нюансы ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-rabotaet-sharding-v-bazah-dannyh-">Как работает Sharding в базах данных?</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[NoSQL]]></category>
      <category><![CDATA[Дата-центр]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 15 May 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда данных становится слишком много для одного сервера, на помощь приходит шардинг — способ разбить базу на части и разложить их по разным машинам. Это помогает масштабироваться, ускоряет запросы и снижает нагрузку. Но вместе с плюсами шардинг приносит и новые сложности: как искать данные, как проводить транзакции между серверами, как считать агрегаты. Сегодня разбираемся, как всё устроено, какие бывают подходы к шардингу и что нужно учесть при его внедрении.</p><h2>Как работает database sharding</h2><p>Database sharding (шардирование базы данных) — это техника горизонтального масштабирования, при которой большая база разделяется на несколько частей. Их называют шардами. Эти шарды распределяются по другим серверам и связываются в одну систему. Рассмотрим подробнее.</p><h3>Принцип: разбиение данных на независимые сегменты (шарды)</h3><p>Каждый шард работает как отдельная независимая база данных. Ключевым элементом здесь выступает ключ шардирования (shard key). Это правила, которые определяют, в какой именно шард попадёт конкретная строка данных.</p><p>Например, мы можем задать правило: если у нас есть user_id и его значение меньше тысячи, то данные попадают в шард 1, если значение больше тысячи, то в шард 2.</p><p>Главная цель такого разделения — добиться независимости шардов. В идеале запрос, касающийся данных одного пользователя (или одного документа, заказа и т. д.), должен обрабатываться только одним шардом.</p><p>Так, мы можем параллельно обрабатывать много запросов и увеличивать пропускную способность системы.</p><h3>Общая архитектура: клиент — роутер — шард</h3><p>Чтобы приложение могло понять, в какой шард отправить запрос, нужна особая архитектура.</p><p><b>Клиент</b>: Программа на стороне пользователя, которая отправляет стандартный запрос к базе данных (например, SELECT * FROM users WHERE user_id = 123).</p><p><b>Маршрутизатор запросов</b> или роутер (Query Router): Это что-то вроде посредника между клиентом и шардом, который выполняет роль диспетчера. Он принимает запрос, при помощи sharding ключа определяет, что это за данные, в какой шард и с какой целью их надо отправить. Далее он отправляет запрос на нужный шард.</p><p><b>Шард (Shard)</b>: Получает запрос, выполняет и возвращает результат обратно маршрутизатору, который затем передаёт его клиенту.</p><p>Благодаря такой архитектуре мы можем «скрыть» database sharding на стороне клиента и обеспечить централизованное управление запросами. Не надо сильно заморачиваться с кодом и архитектурой приложений, ведь вся логика будет на серверах.</p><h3>Основные компоненты: шард, маршрутизатор, реплика-сеты</h3><p>Мы уже рассмотрели, что такое шарды и маршрутизаторы, теперь обратим внимание на реплика-сеты. Это сервера с копией данных шардов. Если главный сервер шарда выходит из строя, одна из реплик автоматически берёт на себя его роль. Так, мы можем повысить отказоустойчивость нашей системы.</p><p>Ещё в этой схеме обычно применяют серверы конфигурации. Это отдельный компонент, который хранит метаданные о шардах. Он содержит информацию о том, какие диапазоны ключей или хеши какому шарду соответствуют. Маршрутизаторы периодически обращаются к серверам конфигурации, чтобы получить актуальную карту распределения данных и лучше понять, в какой шард направить тот или иной запрос.</p><h2>Виды шардинга</h2><p>Существует много вариантов, как разбить базу данных на шарды. Этот выбор будет зависеть от множества факторов: структуры данных, типичных запросов, требований к производительности и сложности управления. Разберём основные виды шардинга.</p><h3>Горизонтальный sharding (по строкам): самый популярный</h3><p>При горизонтальном шардинге мы «нарезаем» нашу базу данных по строкам. Допустим, у нас таблица с клиентами. Мы задаём диапазон ключу шардирования с user_id от 1 до 1 000 000. Данные в этом диапазоне, построчно будут храниться в шадре 1. Если user_id попадает в диапазон от 1 000 001 до 2 000 000, то эти данные отправляем на шадр 2. И так далее.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-30/ced7c0e7-fcbb-4f28-870b-91682a38f689.png" alt="Что такое Sharding" /><figcaption>Горизонтальный шардинг</figcaption></figure><p>Благодаря этому способу мы можем равномерно распределять данные, и нам будет проще масштабировать систему при помощи создания новых шадров и добавления серверов.</p><p>Горизонтальный sharding — идеальный вариант, когда основная проблема — это огромное количество строк в таблицах и высокая нагрузка на чтение/запись.</p><h3>Вертикальный шардинг (по столбцам): разделение по функциональности</h3><p>Если горизонтальный шардинг режет таблицу поперёк (по строкам), то вертикальный — вдоль, разделяя столбцы. Таблица делится на несколько с меньшим количеством столбцов. Обычно они группируются по частоте использования или по смысловой нагрузке. Например, в таблице юзеров можно выделить часто запрашиваемые user_id, username, email в одну таблицу, а редко используемые — biography, preferences, last_login_details — в другую.</p><p>Это полезно, когда у таблицы очень много столбцов или когда группы столбцов имеют совершенно разные паттерны доступа. Мы можем улучшить производительность запросов, так как они работают с таблицами меньшей ширины.</p><h3>Directory-based sharding: использование хеш-таблицы маршрутов</h3><p>При горизонтальном и вертикальном шардинге маршрутизатор часто сам, при помощи специальных функций, определяет, какие данные в какой шадр отправить. Это не всегда удобно. При таком подходе мало гибкости. Поэтому был придуман вид шардинга, который опирается на хеш-таблицы и называется directory-based. Его суть в том, что мы создаём централизованный каталог, который связывает наши ключи шардирования и шарды.</p><p>Вот пример того, как это работает:</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-30/8dc151d5-591c-4a6c-8564-898afc26aaa1.png" alt="Как работает шардинг в базах данных" /><figcaption>Directory-based sharding</figcaption></figure><ul><li>Клиентское приложение отправляет запрос (например, получить данные для order_id = 98765).</li><li>Маршрутизатор запросов перехватывает его.</li><li>Маршрутизатор обращается к каталогу: с запросом, где найти order_id = 98765.</li><li>Каталог ищет в своей таблице соответствий правило, под которое подпадает order_id = 98765. Для быстрого поиска он часто использует эффективные структуры данных, такие как хеш-таблицы или B-деревья (это внутренняя деталь реализации самого каталога).</li><li>Допустим, каталог находит правило «Диапазон order_id 90000-99999 → Shard-3» и сообщает это маршрутизатору.</li><li>Маршрутизатор перенаправляет исходный запрос на Shard-3.</li></ul><p>При таком подходе удобнее перемещать данные между шардами, изолировать их и менять логику.</p><h3>Range-based sharding: разбиение по диапазонам значений</h3><p>По сути, это тот же горизонтальный sharding с разбиением данных на кусочки по строкам при помощи диапазонов.</p><p>Администратор системы (или автоматизированный инструмент) определяет границы диапазонов для ключа шардинга. Маршрутизатор получает запрос, смотрит на значение этого ключа и сравнивает его с известными диапазонами, чтобы определить целевой шард.</p><p>Этот способ простой, логичный и отлично подходит для работы с данными за определённый период. Но у него есть проблема. Допустим, мы запустили форум и проводим шардирование по трём ключам:</p><ul><li>id от 1 до 1000 — шард 1;</li><li>id от 1001 до 2000— шард 2;</li><li>id от 2001 до 3000 — шард 3.</li></ul><p>Когда пользователи начнут регистрироваться, у нас будет активен шард 1, потом шард 2. Далее вся нагрузка перейдёт на шард 3. Они не будут одновременно равномерно работать. Это может стать проблемой при оптимизации.</p><h3>Hash-based sharding: равномерное распределение по хешу ключа</h3><p>Этот подход помогает добиться максимально равномерного распределения нагрузки по всем шардам. Работает следующим образом:</p><p><b>Берём значение ключа</b> для конкретной строки данных (например, user_id = 2001).</p><p><b>Применяем к нему хеш-функцию</b>. Хеш-функция (например, MD5, SHA-1, MurmurHash) — это алгоритм, который превращает входные данные (наш user_id) в строку или число фиксированной длины (хэш), которое выглядит почти случайно. Даже небольшое изменение на входе (например, user_id = 2001 и user_id = 2002) обычно даёт совершенно разные хеши.</p><p><b>Вычисляем номер шарда</b>. Чаще всего делим значение хеша на количество шардов и берём остаток от деления. Например, 548291 % 3 = 2. Далее в зависимости от остатка распределяем данные по шардам. Значение с остатком 2 пойдёт в шард 2, если остаток 1, то в шард 1 и так далее.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-30/bb6c10d3-0c7a-4499-ab9a-11d2d3c34be0.jpg" alt="Что такое Sharding" /><figcaption>Hash-based sharding</figcaption></figure><p>Так мы получаем более равномерное распределение данных. Но из минусов — в такой базе сложно обрабатывать диапазоны и добавлять новые шарды в систему.</p><h2>Примеры реализации</h2><h3>MongoDB: встроенная поддержка шардинга</h3><p>MongoDB — это NoSQL база данных, которая была разработана с закосом на горизонтальное масштабирование. То есть она адаптирована к тому, чтобы работать на нескольких серверах и расширять это число по мере необходимости. Поэтому sharding здесь доступен из коробки, нам не надо заморачиваться со внешними расширениями.</p><p>Рассмотрим пример, как настроить sharding и работать с ним:</p><ul><li>sh.addShard() — добавляем сервер в кластер как шард. MongoDB понимает, что может использовать этот сервер для хранения части данных.</li><li>use socialApp; sh.enableSharding("socialApp") — переключаемся на базу данных «socialApp». При помощи команды sh.enableSharding() разрешаем шардинг в этой БД.</li><li>sh.shardCollection("socialApp.users", { "user_id": 1 }) — применяем sharding к коллекции users. Мы указываем полное имя коллекции (socialApp.users) и ключ шарда ({ "user_id": 1 }). Цифра 1 означает, что мы используем шардинг по диапазонам. Эти диапазоны не нужно задавать вручную, MongoDB определяет их самостоятельно.</li><li>db.users.insertMany([...]) — вставляем данные в коллекцию users. MongoDB автоматически определяет, на какой шард поместить каждого пользователя.</li><li>db.users.findOne({ user_id: 1 }) — запрашиваем данные пользователя user_id = 1. Mongos сам определит, на каком шарде они находятся.</li></ul><h3>PostgreSQL + Citus</h3><p>В PostgreSQL нет поддержки встроенного шардинга, поэтому приходится устанавливать на сервер Citus. Это популярное расширение, которое как раз добавляет возможности горизонтального масштабирования и шардинга. Оно превращает кластер стандартных серверов PostgreSQL в распределённую базу данных.</p><p>Посмотрим на пример использования SQL-команд для настройки шардинга с помощью Citus:</p><ul><li>CREATE EXTENSION citus; — активируем Citus в текущей базе данных.</li><li>CREATE TABLE app_logs (...) — создаём таблицу app_logs на узле-координаторе точно так же, как создали бы обычную таблицу в PostgreSQL.</li><li>SELECT create_distributed_table('app_logs', 'service_name'); — при помощи этой команды включаем sharding. Citus понимает, что таблицу app_logs нужно распределить по рабочим узлам, используя поле service_name как ключ шардинга. Citus по умолчанию применяет Hash-based sharding, то есть, равномерно распределяет по шардам при помощи хеш-функций.</li><li>INSERT INTO app_logs — добавляем данные логов. Citus перехватывает запрос, вычисляет хеш от значения service_name для каждой строки и распределяет данные по шардам.</li><li>SELECT ... FROM app_logs — запрашиваем данные. Если фильтруем по service_name (как в первом SELECT), Citus направит запрос на нужный шард.</li></ul><h3>MySQL + Vitess</h3><p>MySQL — ещё одна система управления базами данных, у которой по умолчанию нет поддержки шардинга. Ситуацию спасает Vitess. Это система кластеризации баз данных — что-то вроде сторонней платформы, которая помогает организовать работу в рамках кластера.</p><p>Vitess вместо прямых команд для шардинга определяет его правила в отдельном JSON-файле, который называется VSchema (Vitess Schema).</p><p>Допустим, у нас есть база данных commerce. Вот как она выглядит в виде кода:</p><p>А вот так она выглядит в виде таблицы:</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-30/1dd751fd-aa6e-49f1-9883-6431fa53b08f.png" alt="Как работает шардинг в базах данных" /><figcaption>Пример структуры базы данных</figcaption></figure><p>В базе данных есть таблица orders, её мы и хотим шардировать по customer_id при помощи хеширования (hash-based sharding).</p><p>Файл VSchema (vschema.json) может выглядеть примерно так:</p><ul><li>"sharded": true — указываем, что база данных шардирована.</li><li>"vindexes" — определяем hash_index типа hash, который будет использовать хеш-функцию для распределения.</li><li>"tables" — описываются правила для конкретных таблиц.</li><li>"orders" — настраиваем таблицу orders.</li><li>"column_vindexes" — определяем, какой столбец является ключом ("column": "customer_id") и какой vindex ("name": "hash_customer_id") использовать для него.</li></ul><h2>Проблемы шардирования</h2><h3>Перераспределение шардов (resharding)</h3><p>Со временем может потребоваться изменить количество шардов или способ разделения данных. Например, добавить серверы для увеличения мощности или изменить ключ шардирования. Этот процесс называется решардингом.</p><p>При решардинге часто приходится перегонять большие объёмы данных между серверами. Если используется хеш-шардинг, и мы меняем количество шардов, то пересчитываются ключи, по которым распределяются данные.</p><p>Например, раньше у нас было 4 шарда. Мы брали user_id, например 9, делили на 4 и получали остаток — 1. Значит, данные шли на шард номер 1. После масштабирования стало 5 шардов, и 9 % 5 = 4 — теперь те же данные должны храниться на шардe 4.</p><p>Такое перемещение затратно по ресурсам: грузит сеть, диски, может занять часы или даже дни. Иногда для этого приходится останавливать запись, что делает процесс ещё рискованнее.</p><h3>Неравномерная нагрузка (hot shards)</h3><p>Вспомним вид шардирования Range-based sharding. Это когда мы разбиваем данные по диапазонам. У нас был пример выше с форумом и шардингом user_id:</p><ul><li>id от 1 до 1000 — шард 1;</li><li>id от 1001 до 2000— шард 2;</li><li>id от 2001 до 3000 — шард 3.</li></ul><p>Мы столкнулись с проблемой, что шард 1 и шард 2 после завершения диапазонов простаивали, а вся новая нагрузка приходилась на шард 3.</p><p>Такую проблему называют горячим шардом (hot shards). Производительность всей системы начинает зависеть от самого загруженного шарда. Преимущества шардирования снижаются.</p><p>Чтобы этого избежать, приходится либо более въедливо продумывать ключи шардирования, либо вручную разделять слишком нагруженный шард.</p><h3>Сложности с глобальными транзакциями и агрегациями</h3><p>Допустим, нужно перевести деньги со счёта А на счёт Б — операция состоит из двух шагов: списание и зачисление. Транзакция позволяет объединить их в единое целое: либо всё выполнится, либо ничего. Это защищает от потери данных и ошибок. В классических базах данных за это отвечают принципы ACID — они гарантируют надёжность в пределах одного сервера.</p><p>Но если счёт А находится на одном шарде, а счёт Б — на другом, стандартная транзакция не сработает. Между независимыми серверами нельзя просто так обеспечить ACID-гарантии. Для этого нужны сложные и медленные механизмы координации, например двухфазный коммит (2PC). Либо приходится идти на компромисс и использовать eventual consistency — когда данные приходят в согласованное состояние с небольшой задержкой. Например, деньги уже списались, но ещё не зачислились.</p><p>Аналогичная проблема и с агрегациями: посчитать сумму продаж или число пользователей уже не получится одной SQL-командой. Каждый шард сначала считает свою часть, а потом результат нужно собрать и объединить — это требует дополнительной координации и может усложниться при фильтрации или группировках.</p><h3>Сложность управления и мониторинга</h3><p>Шардированный кластер — это распределённая система, управлять которой сложнее, чем одним сервером.</p><p><b>Больше компонентов</b>: Вместо одной базы данных появляется множество шардов (часто с репликами), маршрутизаторы запросов, серверы конфигурации. Всё это нужно настраивать, обновлять и обслуживать.</p><p><b>Мониторинг</b>: Требуется отслеживать состояние каждого шарда, равномерность распределения нагрузки, задержки в сети, работу маршрутизаторов. Нужны более сложные инструменты мониторинга.</p><p><b>Отладка</b>: Найти источник проблемы в распределённой системе сложнее. Ошибка может быть где угодно: в приложении, маршрутизаторе, на одном из шардов или в сети.</p><p><b>Резервное копирование</b>: Создание согласованных резервных копий и восстановление данных со множеством независимых шардов требует более сложных процедур.</p><h2>Когда стоит применять Sharding</h2><h3>Рост объёма данных и нагрузок</h3><p>Sharding стоит применять, если БД сильно разрослась, хранить её на одном сервере становится сложно и дорого. Серверу приходится обрабатывать много данных, и поэтому увеличивается время отклика.</p><p>Иногда серверу приходится одновременно читать и записывать слишком много запросов. Система становится перегруженной, пользователи замечают задержки и нестабильную работу. Здесь тоже поможет sharding.</p><h3>Не справляется один сервер/реплика</h3><p>Допустим, у нас один сервер и он не справляется с запросами и большими данными. Конечно, мы можем заняться вертикальным масштабированием, поставить CPU производительнее, добавить больше ОЗУ и так далее. Однако у этого подхода есть ограничения. Во-первых, каким бы мощным ни был бы сервер, он со временем упрётся в потолок своей производительности. Во-вторых, иногда дешевле купить несколько новых серверов помощнее, чем прокачивать старый. Поэтому создание кластера и распределение в нём нагрузки при помощи шардинга — часто более выигрышное решение, чем один сервер.</p><h3>Потребность в геораспределённости или отказоустойчивости</h3><p>Если пользователи находятся в разных частях света, то деление данных и хранение ближе к ним может ускорить работу приложения. Мы можем при помощи шардинга размещать данные в разных дата-центрах, ближе к определённым группам пользователей. Например, так делает Youtube. Компания размещает свои сервера в разных странах, чтобы видео прогружались быстрее и в более высоком качестве.</p><p>Мы можем хранить все данные и обрабатывать запросы на одном сервере и сделать его реплику с копией данных. Если этот сервер падает, то его заменяет реплика. Но что, если эта реплика тоже упадёт? Тогда нам и помогает sharding с репликацией. В случае сбоев отвалятся только отдельные серверы с некоторыми наборами данных. Конечно, здесь тоже, в теории, можно положить весь кластер, включая реплики, но сделать это сложнее.</p><p>Шардирование — полезный инструмент при работе с СУБД. А узнать про большее число подобных инструментов можно в нашем <a href="https://t.me/+qUpIFTBINgJkOGJi">телеграм канале</a>!</p>]]></content:encoded>
    </item>
    <item>
      <title>Веб-разработка и фронтенд: гайд от Tproger</title>
      <link>https://tproger.ru/articles/veb-razrabotka-i-frontend--gajd-ot-tproger</link>
      <comments>https://tproger.ru/articles/veb-razrabotka-i-frontend--gajd-ot-tproger?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Анна Ельцова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/veb-razrabotka-i-frontend--gajd-ot-tproger</guid>
      <description><![CDATA[<p>Собрали топовые материалы по веб-разработке и фронтенду. Рассказываем, что нужно знать новичкам и опытным специалистам в 2025 году.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/veb-razrabotka-i-frontend--gajd-ot-tproger">Веб-разработка и фронтенд: гайд от Tproger</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Мобильная разработка]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 09 May 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Мы заметили, что веб-разработка и фронтенд — самая популярная тематика на Tproger. И, кажется, в айти в целом. Внутри гайда — полезные материалы по стеку с множеством инструментов.</p><ol><li><a href="https://tproger.ru/articles/topovye-instrumenty-dlya-frontend-razrabotki-v-2025-godu">Топовые инструменты для фронтенд-разработки в 2025 году</a> — собрали самые полезные инструменты, которые помогут фронтендерам работать быстрее и лучше в 2025 году.</li><li><a href="https://tproger.ru/articles/reactjs-na-izi--chto-realno-nuzhno-znat-frontend-razrabotchiku-v-2025-godu">ReactJS на изи: что реально нужно знать фронтенд-разработчику в 2025 году</a> — кратко и по делу: что должен знать разработчик, работающий с React в 2025 году. Хуки, компоненты, архитектура.</li><li><a href="https://tproger.ru/articles/frontend-2024?utm_source=tproger&amp;utm_medium=pinned&amp;utm_campaign=tag&amp;utm_term=web">Что должен знать уважаемый фронтендер в 2024 году</a> — обновленный список знаний для фронтенд-разработчиков. Современные фреймворки, DevTools и подходы.</li><li><a href="https://tproger.ru/articles/frontend-razrabotka--chem-zanimayutsya-i-skolko-zarabatyvayut-specialisty">Фронтенд-разработка: чем занимаются и сколько зарабатывают специалисты</a> — Объясняем, чем занимается фронтенд-разработчик, какие бывают задачи и сколько можно зарабатывать.</li><li><a href="https://tproger.ru/articles/kak-rabotat-s-json-v-veb-razrabotke-">Как работать с JSON в веб-разработке</a> — простое объяснение JSON: как он устроен, как его использовать и почему без него не обойтись на фронтенде.</li><li><a href="https://tproger.ru/articles/kak-vybrat-ide--esli-vy-nachinayushhij-veb-razrabotchik">Как выбрать IDE, если вы начинающий веб-разработчик</a> — обзор самых удобных и популярных сред разработки для начинающих веб-разработчиков, их плюсы и минусы.</li><li><a href="https://tproger.ru/articles/kak-effektivno-optimizirovat-bolwoj-obem-dannyh-vo-frontende">Эффективные инструменты для оптимизации кода во фронтенд разработке</a> — собрали инструменты, которые помогут ускорить загрузку, уменьшить размер и повысить читаемость кода.</li><li><a href="https://tproger.ru/articles/30-samyh-poleznyh-bibliotek-python-dlya-veb-razrabotki-v-2024-godu">30 самых полезных библиотек Python для веб-разработки в 2024 году</a> — лучшая подборка Python-библиотек для веба: от фреймворков до утилит. Что взять в 2025 году.</li><li><a href="https://tproger.ru/articles/na-kakom-yazyke-pisat-sajt-v-2024-godu">На каком языке писать сайт в 2024 году</a> — помогаем выбрать язык программирования под сайт в 2025 году. Сравнение по скорости, удобству и экосистеме.</li></ol><p>Предыдущие подборки лежат здесь: <a href="https://tproger.ru/articles/bolwoj-gajd-po-python-ot-tproger--topovye-instrumenty-dlya-raznyh-napravlenij">Python</a>, <a href="https://tproger.ru/articles/bolwoj-gajd-po-react-ot-tproger--topovye-stati-i-instrumenty">React</a>, <a href="https://tproger.ru/articles/bolwoj-gajd-po-mobilnoj-razrabotke-ot-tproger--poleznye-stati--praktiki-i-sovety">мобильная разработка</a>, <a href="https://tproger.ru/articles/s----vse-samye-vazhnye-materialy-ot-tproger">С++</a>, <a href="https://tproger.ru/articles/bolwoj-gajd-po-instrumentam-dlya-razrabotchikov-ot-tproger--frejmvorki--bazy--ai-i-devops-v-odnoj-podborke">инструменты</a>.</p><p>Кстати! Забрать все самые топовые нейронки для айтишников можно в нашем <a href="https://tprg.ru/LN8a">большом гайде с 70+ ИИ-инструментами </a></p>]]></content:encoded>
    </item>
    <item>
      <title>PostgreSQL vs. ClickHouse vs. DuckDB: какую опенсорс базу выбрать для аналитики в 2025 году?</title>
      <link>https://tproger.ru/articles/postgresql-vs--clickhouse-vs--duckdb--kakuyu-opensors-bazu-vybrat-dlya-analitiki-v-2025-godu-</link>
      <comments>https://tproger.ru/articles/postgresql-vs--clickhouse-vs--duckdb--kakuyu-opensors-bazu-vybrat-dlya-analitiki-v-2025-godu-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Владислав Устинов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/postgresql-vs--clickhouse-vs--duckdb--kakuyu-opensors-bazu-vybrat-dlya-analitiki-v-2025-godu-</guid>
      <description><![CDATA[<p>В этой статье сравним и разберём опенсорные СУБД для задач, связанных с аналитикой, на понятном для новичков языке.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/postgresql-vs--clickhouse-vs--duckdb--kakuyu-opensors-bazu-vybrat-dlya-analitiki-v-2025-godu-">PostgreSQL vs. ClickHouse vs. DuckDB: какую опенсорс базу выбрать для аналитики в 2025 году?</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 15 Apr 2025 10:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>В 2025 году выбор правильной открытой базы данных для аналитики становится критически важным для успеха любого проекта. PostgreSQL, ClickHouse и DuckDB — три лидера в этой области. PostgreSQL славится своей надежностью и гибкостью, ClickHouse — высокопроизводительными аналитическими возможностями, а DuckDB — простотой использования и интеграции. Сегодня мы рассмотрим, какая из этих баз данных лучше всего подойдет для аналитиков в текущем году.</p><h2>PostgreSQL: Универсальная объектно-реляционная СУБД</h2><p>PostgreSQL — объектно-реляционная СУБД (система управления базами данных). Термин реляционная означает, что мы записываем данные в виде взаимосвязанных таблиц со строгой схемой. В этой схеме у нас есть столбцы и ячейки, где занесены значения определённого типа: строки, числа, даты. Прямо как в Excel.</p><p>«Объектность» PostgreSQL заключается в том, что в качестве значений мы можем записывать не только числа, строки, даты, но и сложно структурированные данные, например, JSON или XML файлы. Ещё здесь есть наследование, мы можем создавать дочерние таблицы, которые повторяют структуру и данные родительских. Другая фишка PostgreSQL — пользовательские типы. В нём допускается прописывать свои типы данных и работать с ними. По логике это немного напоминает ООП с классами, объектами и наследованием.</p><p>Есть возможность писать свои функции для обработки данных, подключать сторонние библиотеки, причём делать это на разных языках. В общем, с расширяемостью и гибкостью здесь всё очень неплохо.</p><p>PostgreSQL используют в интернет-магазинах, финансовых системах, банковских и геопространственных приложениях, системах управления контентом в блогах, социальных сетях и аналитике. Это универсальный гибкий инструмент.</p><p>Вишенка на торте — PostgreSQL доступен с открытым исходным кодом. Его можно установить, не заморачиваясь с лицензиями и оплатой, настроить под себя и пользоваться.</p><h2>ClickHouse: Скорость и аналитика от Яндекса</h2><p>ClickHouse родился в стенах Яндекса в середине 2000-х. Изначально это была внутренняя разработка для Яндекс.Метрики — чтобы быстро обрабатывать миллиарды событий, подсчитывать статистику и выдавать отчёты, не теряя в скорости. В 2016 году разработку опубликовали на GitHub с опенсорс лицензией.</p><p>Особенность ClickHouse заключается в его колоночно-ориентированной архитектуре. Традиционные СУБД работают с данными построчно. Это значит, что каждая строка — как бы отдельная сущность с данными.</p><p>Допустим, нам надо считать данные с одного столбца. Классические СУБД пойдут искать ячейки столбца по строкам, вместо того, чтобы выбрать этот столбец.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-09/5154a022-f01e-47bc-a8b3-6fb0fc41dea3.jpg" alt="" /><figcaption>Пример логики работы строковой СУБД</figcaption></figure><p>ClickHouse спроектирован иначе. Он хранит данные в столбцах, а не в строках. Благодаря этому в задачах с аналитикой, нам не надо считывать все строки, чтобы добраться до конкретной ячейки в столбце. Мы считываем меньше ячеек, тратим меньше ресурсов и получаем большую производительность.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-09/f3031a64-68a7-4bac-900d-0895bd51cd86.jpg" alt="" /><figcaption>Пример логики работы колоночной СУБД</figcaption></figure><p>ClickHouse поддерживает векторизацию. Это значит, что он нарезает БД (базу данных) на векторы — отдельные куски. СУБД сразу обрабатывает весь кусок целиком, вместо того чтобы по очереди заниматься каждой ячейкой. Здесь есть Simd-оптимизация — технология, которая позволяет процессору выполнять операции сразу над несколькими элементами данных параллельно.</p><p>Благодаря колоночной архитектуре и хорошей оптимизации ClickHouse быстро работает с данными. Его применяют в аналитике Uber, Bloomberg, Google, Netflix, VK, Avito, Т-банк, ну и, конечно же, Яндекс.</p><h2>DuckDB: Локальная аналитика</h2><p>DuckDB — ещё одна СУБД с открытым исходным кодом. Как и ClickHouse использует колоночную архитектуру, векторизацию и Simd-оптимизацию. Благодаря этому, хорошо справляется с аналитическими задачами.</p><p>Главная фишка DuckDB в том, что он предназначен для работы на стороне клиента и оптимизирован под обычные ПК и ноутбуки. DuckDB популярен среди аналитиков, которые работают с данными локально, например, через Python или R. Его можно подключить как библиотеку и работать с данными внутри проекта.</p><h2>Сравнение возможностей СУБД</h2><h3>Производительность в тестах</h3><p>Для <a href="https://benchmark.clickhouse.com/">сравнения </a>я использовал <a href="https://benchmark.clickhouse.com/">ClickBench</a>. Это набор данных с тестами для разных СУБД, весом более 70 гб и количеством около 100 млн строк.</p><p>В качестве оборудования выбрал c6a.4xlarge. Здесь 32 гб. ОЗУ, 16-ядерный процессор и диск объёмом 500 гб.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-09/8b471ba3-6e69-43e7-aa02-d25826208226.png" alt="" /><figcaption>Результаты тестов</figcaption></figure><p>Тест предполагает, что СУБД обрабатывает данные при помощи 42 запросов. Cold Run — обработка некэшированных данных, Hot Run — работа с кэшированными данными, Storage Size — сжатие в гигибайтах (1 Гиб ≈ 1.073 Гб).</p><p>DuckDB оказался самым шустрым в тесте Cold Run, он обрабатывает некэшированные данные за 372 секунды, тогда как у ClickHouse 470 сек., а у PostgreSQL 937 сек.</p><p>Однако, ClickHouse оказывается быстрее в тесте Hot Run — когда данные в кэше. У него 372 сек., тогда как у DuckDB 470 сек. а у PostgreSQL всё те же 937 сек.</p><p>Также ClickHouse лучше всех работает с памятью. Он смог сжать 70 гб. данных до 13.48 гибибайт. На втором месте DuckDB, у него 22.03 Гиб. PostgreSQL не только не сжал данные, но и раздул их до 99.18 Гиб (примерно 106 гб). Возможно, ClickHouse и DuckDB проще сжимать информацию, так как у них колоночный подход к работе с данными, ведь в столбце повторяющиеся значения могут встречаться чаще, чем в строке. Увеличение объёма данных в PostgreSQL возможно вызвано тем, что СУБД наплодило много метатегов к данным.</p><p>В общем, PostgreSQL сильно уступает в производительности ClickHouse и DuckDB. С памятью работает тоже хуже.</p><h3>Масштабируемость систем</h3><h4>PostgreSQL — хороший вариант для вертикального масштабирования</h4><p>Отлично подходит для вертикального масштабирования. Это значит, что если у нас увеличивается нагрузка и сервер уже не вывозит, то мы можем просто его прокачать: купить побольше ОЗУ, диск, процессор помощнее. Либо можно купить другой более мощный сервер и перенести данные на него.</p><p>Такова особенность классических реляционных моделей, они изначально проектировались с учётом вертикальной масштабируемости.</p><p>Обратная сторона — проблемы с горизонтальным масштабированием. Это когда мы покупаем несколько серверов послабее, распределяем между ними нагрузку и создаём кластер. Чтобы такое провернуть, понадобится дополнительная настройка через инструменты вроде Citus или Pgpool.</p><h4>ClickHouse масштабируется вертикально и горизонтально</h4><p>Тоже хорошо подходит для вертикального масштабирования, как и PostgreSQL. Однако он также поддерживает шадрирование и репликацию. Шадрирование означает, что  ClickHouse может нарезать базу данных на кусочки и распределять эти кусочки между разными узлами. Репликация означает, что СУБД может копировать их на другие сервера в кластере.</p><p>Благодаря этому ClickHouse также хорошо подходит и для горизонтального масштабирования, когда нагрузка распределена на несколько серверов. Но есть нюанс. Здесь может быть задержка при синхронизации между узлами и сложности с изоляцией транзакций.</p><h4>DuckDB — только вертикальное масштабирование на обычных ПК и ноутбуках</h4><p>Мы можем установить DuckDB на обычный ПК и прокачивать компьютер сколько угодно. В этом плане с вертикальным масштабированием никаких проблем нет. Но вот создать кластер и масштабировать горизонтально не получится, так как СУБД предназначена для индивидуального локального использования.</p><h3>Экосистема и удобство использования</h3><p><b>PostgreSQL </b>выделяется своей зрелой экосистемой. Он кроссплатформенный, можно установить на Windows, MacOS, Linux, Docker. После установки необходима небольшая настройка под свои задачи.</p><p>Мы можем управлять СУБД через графический интерфейс pgAdmin.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-09/1fce6f0f-5d47-4104-92b5-cd2eaf57968d.png" alt="" /><figcaption>Интерфейс Postgresql</figcaption></figure><p>Он поддерживает создание и редактирование объектов, просмотр структуры, визуализацию схемы. Для этого даже необязательно вводить SQL запросы, хотя это тоже можно. Также есть консольный клиент psql.</p><p>PostgreSQL поддерживает интеграцию с BI системами, например, Tableau, Metabase, Power BI, и расширения наподобие PostGIS и TimescaleDB.</p><p>У PostgreSQL всё хорошо с расширяемостью, мы вправе писать свои функции, индексы, подключать сторонние модули. Здесь можно писать код не только на SQL, но и на Python, C, C++, Java, JS, Rubi, R, Lua.</p><p>Документация — одна из лучших, а сообщество активно помогает на форумах и конференциях. Однако для аналитики нужна оптимизация, импорт больших данных может быть медленным.</p><p><b>ClickHouse </b>ориентирован на аналитику больших данных в реальном времени, что отражается в его экосистеме. Поддерживает установку на Windows, MacOS, Linux, Docker.</p><p>ClickHouse готов к работе «из коробки» для аналитических задач. Можно ничего не настраивать и сразу создать таблицу, но при условии, что СУБД будет работать только на одном устройстве. Если мы собираемся запустить ClickHouse на кластере, то придётся немного повозиться с настройками шадринга и репликации. Есть поддержка как SQL, так и других языков, но только через клиентские библиотеки.</p><p>Документация подробная, но сообщество меньше, чем у PostgreSQL. Разных наворотов тоже меньше, поэтому ClickHouse больше про минимализм. Есть много вариантов графических интерфейсов от сторонних разработчиков.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-09/7d7fe2cd-fe50-4481-9c5f-ee806bb9bae5.png" alt="" /><figcaption>Сторонний интерфейс agx</figcaption></figure><p>Например, на изображении интерфейс <a href="https://github.com/agnosticeng/agx/tree/main">agx</a>.</p><p>DuckDB можно установить на Windows, MacOS, Linux. Установка напоминает подключение библиотеки к проекту. Просто пишем команду pip install duckdb для Python или install.packages("duckdb") для R и всё установлено локально, без сервера. Поддерживает и другие языки через клиентские библиотеки.</p><p>Он разработан с акцентом на простоту и локальное использование, поэтому здесь можно сразу начать работу, без настройки.</p><p>В марте 2025 года у DuckDB появился свой графический интерфейс. В нём тоже можно писать SQL запросы, смотреть результаты и организовывать проекты в виде блокнотов.</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-04-09/c4e05f6f-0653-48c3-a2b0-1126f16b524d.png" alt="" /><figcaption>Интерфейс DuckDB</figcaption></figure><p>Также есть интерфейсы от сторонних разработчиков и консольный клиент.</p><h3>Поддержка транзакций (ACID)</h3><p>ACID — это набор свойств, которые обеспечивают надёжность транзакций в базах данных:</p><ul><li><b>Atomicity (Атомарность)</b>: Все операции в транзакции выполняются полностью, или ни одна из них не выполняется. Если что-то идёт не так, изменения откатываются.</li><li><b>Consistency (Согласованность)</b>: После каждой транзакции база данных остаётся в согласованном состоянии, соблюдая все ограничения (например, уникальность ключей, внешние ключи).</li><li><b>Isolation (Изоляция)</b>: Транзакции изолированы друг от друга, то есть незавершённые транзакции не видны другим.</li><li><b>Durability (Долговечность)</b>: После завершения транзакции все изменения сохраняются, даже в случае сбоя системы.</li></ul><p>ACID особенно важен для OLTP — обработки транзакций в реальном времени. Такие транзакции есть, например, в банковских системах, где требуется строгая согласованность и надёжность. В аналитических сценариях (OLAP) требования к ACID могут быть менее строгими, так как приоритет отдаётся скорости и масштабируемости.</p><p><b>PostgreSQL </b>изначально разработан для OLTP-нагрузок, где транзакционность критически важна. Он полностью поддерживает ACID, но в ущерб скорости обработки данных, что может снижать производительность в аналитических задачах.</p><p><b>ClickHouse </b>жертвует полной поддержкой ACID ради скорости и масштабируемости. Это делает его идеальной СУБД для аналитических задач, но ограничивает использование в OLTP-сценариях.</p><p>Колоночная архитектура позволяет быстро считывать и обрабатывать данные, но вот чтобы их добавлять, изменять, удалять нужно взаимодействовать со строками.</p><p>Когда мы изменяем или удаляем данные, у нас создаётся новая строка, но старая никуда не девается, а просто помечается как изменённая или удалённая. В конце, при слиянии, ClickHouse всё же в фоне удаляет устаревшие данные, но из-за особенностей движка у нас есть промежуточное состояние, когда одновременно есть и старые, и новые строки. Это нарушает принципы атомарности и согласованности. Если представить, что ClickHouse отвечал бы за операции с оплатой в интернет-магазине, мы могли получить ситуацию, когда 2 пользователя купили один товар.</p><p><b>DuckDB</b> балансирует между скоростью (для OLAP) и частичной поддержкой ACID (для надёжности). Он поддерживает транзакции, но с ограничениями. Например, СУБД не может работать сразу с несколькими пользователями, что делает её подходящей для одиночной аналитики, но не для OLTP.</p><p>Из-за колоночной архитектуры здесь также, как и в ClickHouse есть сложности в плане поддержки атомарности.</p><h2>Какую СУБД выбрать для аналитики</h2><p>Выбирая между PostgreSQL, ClickHouse и DuckDB надо определиться, что важнее: транзакционная надёжность, аналитическая скорость или локальная простота.</p><h3>PostgreSQL: для небольших данных и универсальности</h3><p>PostgreSQL подойдёт для аналитики не сильно больших данных, например, в интернет-магазине, при условии, что это не маркетплейс размером с OZON. Это особенно хороший вариант для проектов, где нужна гибкость или поддержка транзакций по стандарту ACID.</p><h3>ClickHouse: для больших данных в реальном времени</h3><p>ClickHouse хороший вариант для аналитиков данных и дата-инженеров, которые работают с большим потоком информации в реальном времени. Идеальный вариант для веб-аналитики. Например, здесь можно анализировать лог сайта с большим числом строк. Но не подойдёт, если нужны ACID транзакции. Быстро читает и обрабатывает данные, но медленно их изменяет, создаёт и удаляет.</p><h3>DuckDB: для локальной аналитики на ноутбуке</h3><p>Подойдёт дата-сайентистам и аналитикам, работающим локально, без настройки сервера. Однако DuckDB не годится для больших объёмов данных, его не получится развернуть на кластере, про поддержку ACID тоже можно забыть.</p><blockquote><b>PostgreSQL </b>отлично подходит для транзакционных приложений, особенно когда нет особенно высоких требований по масштабируемости или скорости. Когда они появляются, нужно будет искать более сложные решения.<br /><br />Кстати, для ИИ-приложений, в частности, для Retrieval-Augmented Generation (RAG), pg_vector для PostgreSQL — довольно популярное стартовое решение.<br /><br />Если нужна «просто СУБД» — PostgreSQL отличное решение «по умолчанию».<br /><br /><b>ClickHouse </b>— хороший выбор для быстрой аналитики. Например, чтобы строить отчёты и графики в BI-инструментах. Популярная также комбинация PostgreSQL и ClickHouse как лидеров в своих нишах. В этой комбинации PostgreSQL отвечает за исходную обработку данных в приложении, затем передаёт данные в ClickHouse, который поддерживает последующую аналитику.<br /><br />Для более специфических задач — ultra-low latency, потоковая обработка данных, комбинированные решения — могут потребоваться другие, более сложные инструменты. Примеры: CockroachDB, YugabyteDB (оба — распределённые версии PostgreSQL), Apache Ignite (для ultra-low latency SQL и транзакции), Apache Druid (для быстрой аналитики).<br /><br /><b>DuckDB </b>— скорее нишевый инструмент для data scientists и data engineers. Например, он подходит для обработки больших объёмов данных в скриптах на Python, когда это нужно сделать локально. Если вы разрабатываете приложения, то, скорее всего, вам DuckDB не нужен.<br /><br />Я много лет разрабатываю распределённые базы данных (GridGain и Apache Ignite). Помог запустить множество систем, использовавших PostgreSQL и ClickHouse, или заменял их своим продуктом.<br />PostgreSQL — отличный продукт, очень гибкий. Но, чтобы раскрыть его потенциал нужна или очень сильная команда, или хороший внешний вендор, поэтому у Postgres Professional всё хорошо. Большинству компаний для нетиповых задач, особенно low-latency/real-time, проще найти альтернативное решение.<br /><br />ClickHouse классно делает ровно одну вещь — быстро обрабатывает SQL запросы на больших данных. Им не решить все задачи аналитики в компании — долгосрочное хранение, трансформация данных и многое другое нужно будет решать другими инструментами.</blockquote><blockquote>PostgreSQL, ClickHouse и DuckDB — три очень разные системы, каждая со своей логикой и зоной применения. Тут не про «что лучше», а скорее — «что подойдёт именно под вашу задачу».<br /><br /><b>PostgreSQL<br /><br /></b>Это универсальный инструмент. Если не знаете, с чего начать — скорее всего, начинать стоит с него. Он хорошо справляется с большинством типовых задач: хранение данных, транзакции, запросы, индексы, API. Работает стабильно, предсказуемо, и его знают почти все разработчики. Проблемы начинаются, когда данных становится очень много, особенно если есть тяжёлые аналитические запросы — тогда он уже не так эффективен. Масштабируется скорее вертикально, и не всегда удобно тюнить для сложной аналитики.<br /><br /><b>ClickHouse<br /><br /></b>Если у вас огромные объёмы данных и вы хотите быстро гонять по ним аналитику — это ваш инструмент. СУБД для чтения, не для транзакций. Отлично показывает себя в задачах мониторинга, логирования, аналитики в реальном времени. Очень быстрая, но требует больше внимания: настроек, понимания архитектуры, знание её ограничений (например, с JOIN'ами и транзакциями). Это уже не «поставил и работает», тут придётся повозиться.<br /><br /><b>DuckDB<br /><br /></b>Это интересный новичок — очень лёгкая, быстрая аналитическая СУБД, которая отлично работает локально, особенно в связке с Python или Jupyter. Я бы назвал её «SQLite для дата-сайентистов». Не требует сервера, можно просто подключить к CSV, Parquet или Pandas DataFrame и писать SQL-запросы. Для быстрого анализа данных — идеально. В продакшене пока использовать её с осторожностью, но для локальной аналитики — инструмент номер один.<br /><br /><b>Когда что выбирать?<br /><br /></b>Если вы делаете обычное приложение — PostgreSQL. ClickHouse — лучший выбор для аналитики в реальном времени, дашбордов, логирования, мониторинга, и других сценариев с интенсивными SELECT-запросами. DuckDB — отличный выбор для дата-сайентистов, аналитиков и быстрой локальной аналитики. Особенно хорош в ситуациях, когда нужно быстро что-то «пощупать» без подъёма инфраструктуры.</blockquote><p>AI, ML, нейросети, аналитика — это явно твоя тема. Поэтому скорее ждем в нашем <a href="https://t.me/+qUpIFTBINgJkOGJi">тг-канале</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>S3-совместимые хранилища: как собрать свой конструктор</title>
      <link>https://tproger.ru/articles/s3-sovmestimye-hranilishha--kak-sobrat-svoj-konstruktor</link>
      <comments>https://tproger.ru/articles/s3-sovmestimye-hranilishha--kak-sobrat-svoj-konstruktor?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ekaterina Davidova]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/s3-sovmestimye-hranilishha--kak-sobrat-svoj-konstruktor</guid>
      <description><![CDATA[<p>В одном из больших кластеров S3 в Точке хранится 110 терабайт полезных данных. Это не много по объёму, но он распределён среди 600+ миллионов файлов. Стоимость работы системы оценивается более чем в миллион рублей в месяц.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/s3-sovmestimye-hranilishha--kak-sobrat-svoj-konstruktor">S3-совместимые хранилища: как собрать свой конструктор</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 09 Apr 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Где хранить данные</h2><p>Когда говорим про хранение данных, первое, что приходит в голову — это локальные диски: жёсткие (HDD) и твердотельные (SSD). Ещё есть базы данных и бэкап. Давайте рассмотрим преимущества и недостатки каждого из вариантов.</p><h2>Локальные диски</h2><p>Чаще всего для хранения используют рейды (RAID) — это способ обезопасить данные, объединив несколько дисков в один массив.</p><p>Например, в RAID 10 (1+0) есть как минимум четыре диска. Две пары из них создают зеркала (RAID 1) и дублируют данные. Поэтому, если два диска выйдут из строя (из разных зеркал), то данные всё равно останутся в сохранности.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/2982632b-c571-4104-ac90-485e305fc33d.png" alt="" /></figure><p><b>Плюсы локальных дисков:</b></p><ul><li>Они очень быстрые.</li><li>Нет задержек сети.</li></ul><p><b>Минусы: </b></p><ul><li>Физически находятся в одной локации, поэтому есть риск потери данных во время масштабной аварии или катастрофе.</li><li>Как правило, использование дисков подразумевает наличие файловой системы и её ограничений.</li></ul><h2>Сетевые диски</h2><p>Если нужно получить доступ к данным не только локально, но и удалённо, используют сетевые диски. Например, на картинке ниже один из способов организовать такой накопитель.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/57cb556e-c936-46d5-94e2-89cb1f31562a.png" alt="" /></figure><p><b>Плюсы сетевых дисков:</b></p><ul><li>Вычислительные устройства находятся отдельно от места хранения данных.</li><li>Возможно множественное подключение.</li><li>Взаимодействие с файлами происходит удалённо — особенно удобно, если они не переписываются часто.</li></ul><p>Но есть и минусы:</p><ul><li>Как правило, нет репликации в реальном времени или она есть, но больше похожа на отложенный бэкап. Такой подход может привести к потере данных.</li><li>Есть конфликты при общем доступе, когда несколько устройств одновременно работают с одним и тем же файлом.</li><li>Ограничения файловой системы на длину имени, размер, количество файлов, символы.</li></ul><h2>Бэкап</h2><p>Его часто упускают из вида, но это тоже способ хранить данные. С его помощью можно скопировать информацию из основной системы в другое изолированное пространство, и, если с данными что-то случится, восстановить их.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/0a1e6be9-3f3e-4b75-84bf-e446325ef60e.png" alt="" /></figure><p><b>Плюсы бэкапа: </b></p><ul><li>Можно хранить практически любой объем и тип информации, потому что бэкапы дедуплицируются и сжимаются.</li><li>Есть разные стратегии создания, в том числе на очень медленные накопители, такие как магнитные ленты.</li></ul><p><b>Минусы бэкапа:</b></p><ul><li>Нет синхронизации с основной системой в реальном времени, нет возможности писать напрямую в бэкап, всегда есть отставание от реальных данных.</li><li>Может быть сложно организовать хорошо работающую систему бэкапирования. Более того, её нужно регулярно проверять, чтобы всё работало и данные можно было восстановить.</li></ul><p>Если приложение может пережить потерю данных за несколько последних часов и простой ему не страшен, то бэкап — лучший вариант относительно стоимости и сложности.</p><h2>Базы данных</h2><p>В традиционных базах есть главный сервер (мастер), который отвечает за запись данных. Когда кто-то хочет добавить или изменить информацию, запросы идут именно к нему.</p><p>А вот чтение может происходить с других серверов (реплик), которые копируют данные. Это позволяет разгрузить главный сервер и повысить скорость доступа.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/12d9e493-e706-4154-b764-833c03f4af80.png" alt="" /></figure><p><b>Плюсов у баз данных много:</b></p><ul><li>Репликация в реальном времени — клиенту не будет возвращён ответ «OK» до тех пор, пока мастер не сделает копию изменений на одну или две реплики.</li><li>Высокая надёжность хранения: данные синхронизированы, копии хранятся в разных физических локациях, поэтому риск потери информации в случае локальной аварии ниже.</li><li>Высокая скорость доступа — большая часть горячих данных хранится в оперативной памяти.</li><li>Есть разные механизмы для ускорения работы, например, индексирование, защита от множественного изменения данных.</li><li>Высокая скорость чтения — нагрузка на систему на чтение может быть распределена между несколькими репликами.</li><li>Нет ограничения файловых систем — миллионы записей укомплектовываются, сжимаются и хранятся в нескольких файлах.</li></ul><p><b>Минусы тоже есть:</b></p><ul><li>Базы данных требуют больше трафика для синхронизации изменений.</li><li>Чувствительны к задержкам сети.</li><li>Не могут расти бесконечно без шардирования, так как диски под каждой репликой имеют ограничение.</li></ul><p>К слову, файловая система — это тоже база данных. У них есть много общих моментов: индексирование, указатели на физическое местоположение данных, блокировки, нет только репликации на несколько хостов. Поэтому чаще всего нет особой разницы в хранении данных на дисках и в базе, ведь файловая система работает примерно так же.</p><h2>Что такое S3 и распределённые хранилища данных</h2><p>Все методы выше по-своему хороши, но у них есть недостатки: базы данных и диски не могут расти бесконечно, с бэкапом нет синхронизации изменений.</p><p>И здесь на помощь приходят распределённые системы хранения данных, которые чаще всего скрываются за аббревиатурой S3 (Simple Storage Service). Они взяли себе всё лучшее из всех перечисленных выше подходов к хранению информации. Такие системы изначально спроектированы под хранение цифровых данных большого объема. У каждого модного провайдера сейчас есть собственный сервис, который предоставляется, как дополнительная услуга.</p><p>На самом деле, распределённые системы хранения — это тоже не универсальный инструмент, но при этом они:</p><ul><li>Подходят практически для любых данных.</li><li>Позволяют хранить разнородную информацию.</li><li>Обеспечивают быстрый доступ и высокую сохранность.</li></ul><p>Ниже под термином S3 будет подразумеваться в первую очередь распределённая система хранения разнородных данных, а не протокол взаимодействия.</p><h2>Преимущества S3</h2><p>Главный плюс распределённых систем хранения, что метаданные больше не лежат вместе с содержимым файлов. И это очень удобно относительно архитектуры, потому что к метаданным обращаются намного чаще, например, когда нужно узнать список файлов, их характеристики, что-то получить, удалить, изменить. А содержимое файлов используют всего в трёх сценариях: когда нужно получить данные, загрузить новые или удалить. Поэтому вместе с разграничением метаданных и чанков содержимого повышается скорость работы системы.</p><p>Кроме того, это решение избавляет нас от ограничений файловой системы, которая обычно хранит мету и данные совместно. В некоторых случаях метаданные могут весить больше самих файлов. То есть, файл настолько мал, что его характеристики больше, чем содержимое. При таком сценарии может случиться ситуация, когда свободное место на диске есть, а записать новые файлы нельзя (закончились ноды).</p><p>Вы, наверняка, замечали, что несколько больших файлов на диске копируются быстрее, чем тот же объём, разделённый на огромное число мелких файлов. Отделение метаданных решает этот вопрос и даёт возможность неограниченного роста файлов без влияния на скорость доступа к ним.</p><h2>Сравниваем разные S3-совместимые системы хранения</h2><p>Изначально при выборе системы хранения мы рассматривали несколько вариантов:</p><ul><li>Minio.</li><li>Ceph.</li><li>SeaweedFS.</li></ul><p>Сравним важные по нашему опыту характеристики, чтобы понять, в чём между ними разница.</p><h2>Внешняя мета</h2><p>Хранение метаданных отдельно критически важно для удобного масштабирования и повышения производительности системы. У Minio, который часто используется в качестве S3, всё хранится на дисках вместе — и мета, и данные. Он полностью зависит от файловой системы, и это плохо, так как в определённый момент с увеличением числа файлов скорость будет замедляться.</p><p>У Ceph, несмотря на то, что всё тоже хранится вместе, есть несколько механизмов, которые позволяют обойти эти ограничения. Но при этом есть сложности с масштабированием метаданных.</p><p>Поэтому оба варианта, основываясь на нашем опыте, не подходят для хранения большого числа сравнительно малых файлов.</p><p><b>Как устроено хранилище меты у SeaweedFS:</b></p><ul><li>Хранилище метаданных работает отдельно.</li><li>Это может быть практически любая база данных со всеми её преимуществами.</li><li>Путь до файла — это абстракция в базе, на самом деле, файл ищется по ID серверов, так мы частично обходим ограничения файловых систем.</li><li>Отдельные папки/бакеты можно вынести в отдельные базы данных, или использовать несколько баз одновременно. Это полезно, если один бакет большой или на него приходится большая часть нагрузки.</li><li>Можно собрать свой конструктор под поставленные задачи.</li><li>Можно отключить листинг, если он не нужен.</li><li>Можно хранить огромное количество маленьких файлов и папок внутри базы.</li><li>Есть преимущества от LIKE, например, поиск по префиксам. Он ускоряет операцию листинга файлов в подпапках и избавляет от необходимости перебирать все вложения.</li></ul><h2>Хранение данных</h2><p>У Minio файлы хранятся в виде файлов на файловой системе — с применением erasure coding (EC) для защиты данных. Поведение зависит от файловой системы, а ещё нужен рестарт кластера при добавлении новых дисков.</p><p>У Ceph своя сложная структура хранения RADOS, но при этом нужна ребалансировка при добавлении дисков.</p><p><b>Свой подход к хранилищу чанков у SeaweedFS:</b></p><ul><li>Нет привязки к условиям добавления новых дисков (любые типы и объёмы), не нужна ребалансировка всего кластера.</li><li>Простое устройство волюм-файла.</li><li>Небольшое число настроек волюм-сервера.</li><li>Файлы хранятся в «суперблоке», внутри которого лежат чанки данных. Таким образом в один «файл» на файловой системе помещаются миллионы других файлов без аффекта со стороны файловой системы и её механизмов.</li></ul><p>Вот так выглядит «суперблок». По сути это большой файл, в который записано много других файликов. Благодаря этому мы не столкнёмся с ограничениями файловой системы, когда файлов станет слишком много. На файловой системе даже при самом худшем сценарии будут храниться сотни файлов.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/cc926f62-94bf-484e-9eaa-aafb0fdf880d.png" alt="" /></figure><h2>Запись данных</h2><p>SeaweedFS по умолчанию записывает данные в две реплики. Если одна копия потеряется или станет недоступна, всегда будет вторая. В случае ошибки при записи данных система попытается записать файл снова. Если запись окажется невозможна, то клиенту вернётся сообщение об ошибке. Это важный этап, который гарантирует, что записанный файл не будет потерян сразу после отправки подтверждения клиенту.</p><p>Ceph тоже по умолчанию создаёт несколько реплик. А ещё они предоставляют дополнительные интерфейсы взаимодействия — блочные устройства и сетевую файловую систему (NFS).</p><p>У Minio реализовано больше опций, совместимых с S3 API. Но добиться синхронной записи в реплику невозможно, даже если её включить (:</p><p>Ниже — скриншот из документации. В понимании разработчиков синхронная и асинхронная запись отличается только тем, когда именно файл будет поставлен в очередь на репликацию.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/5db78509-1c27-4f57-aa02-c6a00c95ee4f.png" alt="" /></figure><p>С учётом правильно выбранного erasure coding и других настроек репликация фактически не нужна. Поэтому вариант с “Site Replication” — не замена EC. И вариативность в этом плане ограничена.</p><h2>Работа с дисками</h2><p>Minio обрабатывает файлы на диске последовательно: репликация, удаление каждого файла происходят по отдельности. А ребалансировка — по триггеру (например, когда добавляется новое хранилище или изменяется нагрузка) или вручную.</p><p>У Ceph удаление может быть отложено. При изменениях в дисковом хранилище нужна массовая ребалансировка данных. Это дополнительная нагрузка на диски и сеть.</p><p><b>Нечто среднее реализовано в SeaweedFS:</b></p><ul><li>Операции восстановления, репликации и ребалансировки выполняются через внешние команды, а не постоянно в фоновом режиме, что снижает нагрузку на сеть и позволяет гибко контролировать процесс.</li><li>Удаление в первую очередь происходит из базы (на диск по возможности ставится метка удаления).</li><li>Удаление мусора (вакуум) из волюм-файлов запускается по триггеру и может включать полную перезапись файла.</li><li>Ребалансировка при авариях происходит по триггеру: только тогда система автоматически перераспределит данные для восстановления нужного фактора репликации.</li><li>Можно быстро удалить коллекции (полный бакет) благодаря «суперблокам». С дисков нужно удалить всего несколько файлов, это очень быстрая и простая операция.</li></ul><h2>Масштабирование</h2><p>Minio поддерживает только одинаковые диски внутри пула и замедляется с ростом файлов. Несмотря на то, что сервис позиционирует себя как S3, фактически он им не является. Это просто сетевое хранилище или сетевой диск.</p><p>Ceph тоже замедляется при увеличении количества мелких файлов. А ещё требует очень хорошей сети на случай ребалансировки кластера. И так как конфигурации достаточно сложные, при настройке легко допустить ошибку.</p><p><b>Что можно отметить в работе  SeaweedFS:</b></p><ul><li>Низкие требования к железу и сети.</li><li>Можно использовать разные базы для хранения метаданных. В том числе можно их комбинировать, менять или использовать несколько баз одновременно.</li><li>Можно просто и быстро добавить новые диски, если нужно больше места для хранения.</li><li>Мастер-сервер хранит мало данных, что делает его быстрым и менее требовательным к ресурсам.</li><li>Компоненты системы можно масштабировать, менять и настраивать отдельно, но это требует знания разных технологий и инструментов.</li><li>На текущий момент восстановление репликации происходит последовательно, из-за чего процесс растягивается во времени, а ресурсы не утилизируются в достаточной степени.</li></ul><h2>Как устроен SeaweedFS</h2><p>SeaweedFS — это opensource продукт, что одновременно плохо и хорошо. Главный плюс для нас — мы сами вносим в него изменения. Минус — их вносят и другие люди, поэтому периодически что-то ломают.</p><p>У SeaweedFS один основной автор — Chris Lu. Именно он принимает решения, как проект будет развиваться дальше. Новые релизы выходят примерно 2 раза в месяц.</p><p>Ещё SeaweedFS написан на Go, который очень любит наша команда инфраструктуры.</p><p>Не буду сильно углубляться в схему взаимодействия компонентов внутри продукта в рамках данной статьи, расскажу про общий сценарий.</p><p>Путь одного файла выглядит примерно как на рисунке ниже:</p><ul><li>Запись идёт в две реплики.</li><li>Волюм-сервера напрямую реплицируют файлы между собой.</li><li>В конце записи в базу данных заносится информация о новом файле.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/75999ac4-d903-44b1-86ec-200526dabc6f.png" alt="" /></figure><p>Как и я говорил, база данных — это самостоятельная сущность, в ней хранятся только метаданные. Напомню, это может быть почти любая база данных. Вы можете выбрать с целью хранения метаданных любой хорошо знакомый вам продукт.</p><p>Центральная часть системы хранения устроена так: мастер-сервер отвечает за поддержание актуальной топологии для поиска файлов и выделения позиций для новых данных, волюм-сервера управляют дисковыми ресурсами и обрабатывают запросы к данным.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/d0cddd3a-16e0-4ad3-9677-47cea5f15909.png" alt="" /></figure><p>Как выглядят метаданные:</p><p>Здесь самое важное — информация о том, где найти файл и какие у него характеристики. Во время чтения данных мы обращаемся к базе, выясняем из описания файла, где он хранится, уточняем из топологии адрес волюм-сервера, который хранит нужный нам суперблок, и затем обращаемся напрямую к волюм-серверу за нужным кусочком данных.</p><h2>Как мы делаем бэкапы</h2><p>Так как мы считаем, что делать бэкапы важно даже для больших и объёмных систем, расскажу какие варианты реализации резервного копирования есть в распоряжении у SeaweedFS.</p><p><b>Здесь есть много разных вариантов:</b></p><ul><li>У каждой базы данных есть собственный механизм создания бэкапов.</li><li>Можем экспортировать метаданные так, как их видит SeaweedFS, например чтобы заменить базу данных или произвести бэкап отдельных бакетов.</li><li>Можем копировать волюм-файлы целиком в бэкап (из-за ограничений файловых систем большие файлы перемещаются быстрее, копировать их проще — и суперфайлы, нам в этом помогают).</li><li>Используем «мгновенный бэкап» — инкрементальный встроенный механизм бэкапа. Когда записываем файл в S3, ещё одна его копия сразу же отправляется на отдельный носитель (дополнительно к записи в две реплики), который тоже потом отправляется в бэкап. Благодаря этому, даже если что-то пошло не так, мы можем быстро восстановить даже самые свежие данные.</li></ul><h2>Главные выводы</h2><p>S3 и распределённые системы хранения — это конструктор из разных программных и архитектурных решений, которые вместе дают все плюсы распределённых систем хранения:</p><ul><li>Можно хранить файлы любого размера.</li><li>Можно хранить потенциально бесконечное количество файлов.</li><li>Можно легко и быстро увеличивать компоненты хранения меты и данных.</li><li>Нет потерь данных.</li><li>Нет деградации скорости доступа при увеличении количества данных.</li><li>Есть механизм автоматического восстановления после аварий и повреждений.</li><li>Система умеет оптимально работать с дисками для продления их жизни — производит запись и удаление отдельно, откладывает удаление, не проводит ненужные перемещения, чтобы не расходовать ресурс.</li></ul><p>Реализовать этот конструктор и добиться желаемого результата нам позволяет SeaweedFS. Сейчас это активно эксплуатируемый, однако не единственный инструмент в нашем проде. Также для лимитированной выдачи блочных сетевых устройств применяем Ceph. Раньше использовали Gluster, но он плохо показал себя на больших объёмах данных, особенно в сценариях с большим количеством файлов.</p><p>Ваш сценарий эксплуатации систем хранения может отличаться, и такие решения, как Ceph и Minio, могут оказаться более удобными и подходящими.</p><p><i>Делитесь своим опытом в комментариях! Где вы храните данные и с какими сложностями пришлось столкнуться?</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Какие есть паттерны в React и для чего они нужны: часть 1</title>
      <link>https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1</link>
      <comments>https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Юсуп Изрипов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1</guid>
      <description><![CDATA[<p>В этой части Юсуп Изрипов рассказывает, что такое Container &amp; Presentational Components, Higher-Order Component (HOC) и паттерн Render Props в React и что с ними делать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1">Какие есть паттерны в React и для чего они нужны: часть 1</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Тимлид]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Apr 2025 10:30:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>В мире React термин «паттерн» означает какой-то проверенный подход к решению задачи, а не шаблон проектирования из классической книги. За годы разработки вокруг React сформировались свои распространённые паттерны — способы организовать компоненты и логику так, чтобы код получался понятным, поддерживаемым и переиспользуемым.</p><p>Меня зовут Юсуп Изрипов, я сеньор разработчик в VK. Работаю над продуктами, которыми ежедневно пользуются миллионы человек.</p><p>В этих статьях я расскажу вам о самых популярных паттернах и приведу примеры кода. Мы рассмотрим, когда каждый из них может пригодиться, а также отметим их плюсы и минусы. Поговорим о классических приёмах вроде контейнеров и HOC, эволюции к хукам, также обязательно рассмотрим новые паттерны появившиеся в последних версиях React.</p><h2>Container + Presentational Components</h2><p>На первом месте работы ещё во время разработки на Vue мы с подачи нашего тимлида решили ввести этот паттерн. Глобально у нас были так называемые «умные» и «тупые» компоненты (или как я тактично называл их на демо — визуальные). Как вы наверняка догадались, роль Container у нас исполняли «умные» компоненты, а роль Presentational — «тупые». В чём, собственно, суть этого паттерна? Слышали выражение «разделяй и властвуй»? Паттерн Container &amp; Presentational Components (контейнерные и презентационные компоненты) ровно об этом: он разделяет логику (данные и взаимодействие с ними) и отображение (UI) на разные компоненты.</p><p>Presentational Components отвечают только за то, как что-то выглядит. Они получают данные через props и отображают их, больше ничего. Это, как правило, чистые функциональные компоненты, часто без собственного состояния (ну разве что мелкий UI-стейт типа «раскрыт ли dropdown»). Им всё равно, каким образом взять список пользователей — они просто ожидают условный props.users и отображают его в соответствии с дизайном.</p><p>Container Components, напротив, знают, что показать и откуда это взять, но не занимаются тем, как это отображается. Они содержат в себе всю логику: могут загрузить данные, подписаться на store или контекст, хранить состояние, а рендерят в презентационных компоненты, передавая им готовые данные. Контейнер может вообще не иметь собственного HTML, кроме того, что приходит от дочернего презентационного компонента. Его задача — это взаимодействие с данными.</p><p>Зачем же нужен такой подход? Во-первых, лучшая разделённость ответственности (UI отдельно, данные отдельно) упрощает понимание и поддержку приложения. Во-вторых, улучшается переиспользование: один визуальный компонент вероятно использовать с разными источниками данных через разные контейнеры. Дизайнеры могут менять внешний вид компонента в одном месте, не затрагивая бизнес-логику. И тестировать тоже проще: можно отдельно протестировать логику контейнера (без верстки) и отдельно визуальный компонент (с моком данных).</p><p>В этом примере UserList не содержит никакого стейта, не подписан на store или контекст, лишь отображает список. Ему всё равно, как и где получают пользователей — просто принимает проп users и выводит его. Контейнер UserListContainer же занимается работой с данными: делает fetch, сохраняет результат в useState, и потом рендерит UserList, прокидывая в него данные. Благодаря такому делению компонент UserList легко переиспользовать — хоть для локальных данных, хоть для данных из Redux или context — достаточно написать другой контейнер.</p><p>Конечно, не всегда нужно городить пару компонентов вместо одного. Этот паттерн полезен, когда приложение растёт: вы начинаете замечать, что пропсы идут через несколько уровней просто транзитом, или один компонент слишком перегружен логикой. Тогда вы «вытаскиваете» логику в контейнер, а UI — в презентационный компонент, и код сразу становится чище. Это не обязательное правило, а приём для рефакторинга по мере необходимости.</p><p>Стоит отметить, что с появлением React-хуков граница между логикой и отображением несколько размылась. Теперь можно выносить логику в кастомные хуки и вызывать их прямо внутри компонента, вместо того чтобы создавать отдельный контейнер-класс, как это делали до 2018 года. Тем не менее, принцип «держи логику отдельно от представления» по-прежнему полезен. Даже с хуками можно структурировать код, разделяя функциональность: написать хук useUsersData() для получения пользователей и применять его в разных компонентах (вместо дублирования запроса).</p><p>Плюсы: чёткое разделение обязанностей, возможность переиспользовать и заменять части независимо (UI-компонент можно переиспользовать с разными данными), облегчение тестирования.</p><p>Минусы: появляется больше файлов/компонентов, чем могло бы быть, что может казаться избыточным для мелких случаев. Иногда чрезмерное дробление на «глупые» и «умные» компоненты лишь усложняет структуру, если паттерн применён не к месту. Как говорится, включайте голову — не каждую кнопку нужно выделять в отдельный контейнер.</p><h2>Higher-Order Component (HOC)</h2><p>Когда я впервые услышал термин HOC, он показался мне чем-то из математики. Но на практике всё горадо прозаичнее: HOC — это всего лишь функция, которая принимает React-компонент и возвращает новый компонент, оборачивая исходный дополнительной функциональностью. Проще говоря, HOC — это «обёртка». Мы помещаем один компонент внутрь другого, чтобы на выходе получить расширенную версию переданного в HOC компонента.</p><p>Зачем это может понадобиться? Представим, у нас есть несколько разных компонентов, и всем им нужно что-то общее: например, обработка ошибок или подписка на внешние данные. Можно было бы скопировать этот код в каждый из компонентов, но куда элегантнее написать HOC один раз и применить ко всем. Классический пример — Redux-функция connect: вы пишете export default connect(mapState)(MyComponent), и ваш компонент получает пропсы из глобального стейта.</p><p>connect — как раз и есть HOC, который инъектирует данные из Redux в компонент, не требуя от вас переписывать все под Redux вручную.</p><p>Создать свой HOC тоже несложно. Супер банальный пример — сделаем HOC, который добавляет компоненту стейт счётчика:</p><p>Здесь withCounter — HOC, он возвращает новый функциональный компонент WithCounter, который внутри себя использует useState и передаёт состояние и функцию увеличения внутрь WrappedComponent. В итоге EnhancedButton — это улучшенная версия ClickButton, которая умеет считать клики, даже если исходный ClickButton об этом не знал.</p><p>Плюсы: один HOC может добавить функциональность множеству компонентов сразу — не надо копировать код везде. Логику обновляется в одном месте (внутри HOC) — и все обёрнутые компоненты получают изменения. HOC можно комбинировать: например, обернуть компонент сначала в HOC, добавляющий тему оформления, потом в HOC, добавляющий логирование, и т.д. В итоге получим компонент, обладающий сразу несколькими дополнительными возможностями.</p><p>Минусы: за такую магию мы платим усложнением структуры. Когда компонентов обёрток становится много, React-дерево раздувается, и возникает эффект «матрёшки». В DevTools вы могли видеть что-то вроде: Connect(withRouter(WithTheme(MyComponent))) — разобраться, что к чему, становится сложнее. Дебаг таких цепочек — тоже удовольствие то ещё, приходится пробираться через несколько уровней абстракций. Кроме того, HOC часто прокидывают пропсы во внутренний компонент, что чревато конфликтами имён (нужно следить, чтобы, например, prop.title от HOC не перезаписал пропс title, который вы передали самому компоненту). Ещё нюанс — HOC усложняют типизацию в TypeScript (надо правильно описывать generic для пропсов), но это выходит за рамки нашей темы.</p><p>React-разработчики со временем несколько охладели к HOC. В официальной документации прямо сказано: «компоненты высшего порядка не так часто используются в современном React-коде». Отчасти их вытеснили хуки, тем не менее, HOC никуда не делись: их продолжают применять сторонние библиотеки — тот же Redux, Relay и другие. Да, и в старом проекте вы почти наверняка встретите хотя бы пару HOC. Поэтому понимать этот паттерн стоит. Просто имейте в виду современные альтернативы и используйте HOC там, где это действительно необходимо.</p><h2>Паттерн Render Props</h2><p>Следующий паттерн я бы назвал «перевёрнутый HOC». Render Props — это подход, когда компонент сам не рендерит что-то внутри себя, а принимает функцию (часто через проп render или просто используя детей как функцию) и вызывает её, чтобы получить содержимое. То есть мы передаём компоненту инструкцию, что именно отрендерить, а он сам обеспечивает, когда и с какими данными вызвать эту инструкцию.</p><p>Представьте компонент &lt;MouseTracker&gt; для отслеживания положения курсора. Классически он может хранить x, y в своём состоянии и отрисовывать, скажем, &lt;p&gt;Mouse at (x, y)&lt;/p&gt;. Но что, если мы хотим переиспользовать эту логику уже с другим UI? Паттерн Render Props предлагает сделать компонент &lt;Mouse&gt;, который не определяет жёстко JSX внутри себя, а вызывает функцию, переданную через проп (или children функцию), передавая ей координаты. Эта функция сама решит, что рисовать. Таким образом, &lt;Mouse&gt; инкапсулирует логику (слежение за мышкой), а отображение делегирует наружу.</p><p>Пример: реализуем компонент-утилиту &lt;FilteredList items={...} filter={...}&gt;, который отображает список на основе передаваемого фильтра. Вместо того чтобы жёстко прописывать разметку элемента списка, сделаем его с render проп через children:</p><p>Здесь &lt;FilteredList&gt; знает, как отфильтровать массив (items.filter(filter)), но не знает, как отрисовать каждый элемент. Вместо этого он вызывает функцию, которую мы передали в качестве дочернего элемента (children), для каждого элемента списка. Эта функция возвращает &lt;li&gt; для каждого item. В результате логика фильтрации инкапсулируется внутри FilteredList, а конкретное отображение списка задаётся извне. Мы могли бы так же использовать этот компонент для массива объектов, отрисовывая, например, товары — достаточно передать другую children-функцию.</p><p>Паттерн Render Props здорово повышает гибкость компонентов. Мы можем переиспользовать &lt;FilteredList&gt; для списков чего угодно — чисел, пользователей, товаров — просто изменяя функцию отображения. Другой пример: компонент &lt;Mouse&gt; может предоставлять координаты курсора, а внешний код решит, просто вывести текст, нарисовать по координатам картинку или вызвать какую-то совершенно другую логику — не нужно делать несколько вариаций компонента для каждого кейса.</p><p>Плюсы: Render Props позволяет компоненту-провайдеру (в примере выше FilteredList является провайдером данных) быть максимально универсальным, а конкретную разметку делегировать наружу. Многие библиотеки воспользовались этим паттерном: например, React Router (до версии 6) позволял вместо компонента страницы передать проп render в &lt;Route&gt; — функцию, которая отрисует JSX на основе параметров маршрута. Formik предлагал компонент &lt;Formik&gt; с функцией-ребёнком для рендеринга формы. Downshift (библиотека для автокомплитов) — тоже классический пример паттерна render props.</p><p>Минусы: главное неудобство — излишний шум в JSX. Код с вложенными функциями бывает тяжело читать. В нашем простом примере всё компактно, но представьте, если у вас будет несколько уровней таких компонентов: &lt;Foo&gt;{foo =&gt; ( &lt;Bar&gt;{bar =&gt; ( ... )}&lt;/Bar&gt; )}&lt;/Foo&gt; — легко получить «оберточный ад» из стрелочных функций прямо в разметке. Это значительно затруднит отладку такого кода при возникновении каких-либо проблем. К тому же, каждый раз при рендере создаётся новая функция, что может негативно сказаться на производительности, если таких компонентов много (React конечно оптимизирует функции в пропсах через механизм сравнения, но всё же). Также возникает неявная связь: внешний код должен знать, какие аргументы ожидает функция. TypeScript конечно помогает, но при чтении кода не сразу видно, что children, например, это не просто элемент, а функция.</p><p>Как и HOC, паттерн Render Props сейчас используется реже. Многие задачи, решаемые через него, теперь элегантнее с точки зрения кода решаются хуками, в официальной документации это также отмечено. Но всё же понимать его нужно, потому что легаси-код и некоторые библиотеки всё ещё работают на нём. Если видите, что компонент принимает функцию в виде пропса (чаще всего называется render или передаваётся через детей), знайте — это он, Render Props.</p><p>В следующей части расскажу про хуки и кастомные хуки, а также про Compound Components и Серверные компоненты и Suspense.</p>]]></content:encoded>
    </item>
    <item>
      <title>Настраиваем паука для сбора данных: как работает фреймворк Scrapy</title>
      <link>https://tproger.ru/articles/nastraivaem-pauka-dlya-sbora-dannyh--kak-rabotaet-frejmvork-scrapy</link>
      <comments>https://tproger.ru/articles/nastraivaem-pauka-dlya-sbora-dannyh--kak-rabotaet-frejmvork-scrapy?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ekaterina Davidova]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/nastraivaem-pauka-dlya-sbora-dannyh--kak-rabotaet-frejmvork-scrapy</guid>
      <description><![CDATA[<p>В Точке мы обучаем наших AI-ассистентов, а для этого нужно много данных. В статье расскажу, как быстро собрать информацию практически с любого сайта при помощи фреймворка Scrapy. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/nastraivaem-pauka-dlya-sbora-dannyh--kak-rabotaet-frejmvork-scrapy">Настраиваем паука для сбора данных: как работает фреймворк Scrapy</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[DPI]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 07 Apr 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>В Точке мы обучаем наших AI-ассистентов, а для этого нужно много данных. В статье расскажу, как быстро собрать информацию практически с любого сайта при помощи фреймворка Scrapy.</p><h2>Зачем компании собирают данные</h2><p>Сегодня в интернете более 1 миллиарда сайтов. На великом и ужасном реддите каждый час появляется более 50 000 новых публикаций. На Github уже опубликовано более 300 млн публичных репозиториев. Всё это — открытые данные, которые можно использовать и, главное — собирать. Разумеется, перед этим проверить условия использования сайта, потому что некоторые из них могут запрещать сбор данных.</p><p><b>Зачем это нужно компаниям:</b></p><ul><li><b>Анализ рынка:</b> для ритейла это возможность изучить клиентов и конкурентов.</li><li><b>Мониторинг сайтов вендоров ПО: </b>некоторые компании выкладывают уязвимости в своих продуктах и делятся возможными решениями.</li><li><b>Создание продукта: </b>собранные данные можно обогатить, прикрутить красивый интерфейс, умный поиск и предложить пользователю новое приложение, типа 2GIS.</li><li><b>Развитие LLM: </b>например, Google в 2024 году заключил контракт с Reddit на сбор данных. Open AI тоже часто говорят о том, что обучают свою модель на открытых источниках, а в ближайшее время хотят подключить ещё и транскрибации с YouTube.</li></ul><p>Как вы поняли, в интернете очень много данных. И если мы хотим их собрать, то нам нужен подходящий инструмент.</p><h2>Что такое Scrapy</h2><p>Это высокоуровневый фреймворк на Python для краулинга и скреппинга сайтов. На GitHub у него больше 53 тысяч звёздочек, 10,6 тысяч форков и много глазиков. А ещё он занимает первое место по тегам #crawling и #scraping.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/e72840e2-833f-411b-bb5f-0ade0d6ea7f7.png" alt="" /></figure><p><b>Есть много причин, почему Scrapy так популярен: </b></p><ul><li>Это фреймворк с готовой архитектурой — там много инструментов, доступных из коробки, которые можно настроить и использовать.</li><li>Он асинхронный — чаще возникает задача замедлить его, чем ускорить.</li><li>У него простые настройки — нужно всего раз подумать над архитектурой, а добавление новых источников будет занимать минимум времени.</li><li>Scrapy удобно дебажить в любой момент, начиная от загрузки страницы и заканчивая сохранением данных в базе.</li><li>Есть продуманные селекторы, доступны CSS, Xpath. Их можно комбинировать или выбрать что-то одно.</li><li>Большое комьюнити и обновляемая документация. Ответы на большинство вопросов можно легко найти в сети.</li></ul><p>Scrapy точно подойдёт вам, если во всех ваших источниках одинаковый формат данных и вы можете унифицировать их обработку и сохранение. Но всё-таки его нельзя назвать универсальным инструментом.</p><p><b>Scrapy будет не лучшим выбором, если: </b></p><ul><li>Нужно собрать малый объем данных или собрать их нужно всего один раз.</li><li>Вам нужно отдать просто сырые html или json, а не парсить и преобразовывать данные.</li><li>Среди источников нет общей структуры данных.</li></ul><p>Во всех этих случаях мы можем использовать Scrapy, но, скорее всего, он будет излишним.</p><h2>Как работает Scrapy</h2><p>После того, как вы установили Scrapy в виртуальное окружение ( '' pip instal scrapy' ) и создали новый проект ( ' scrapy startprogect scraper '' ), вам необходимо написать своего первого «паука».</p><p>В Scrapy используется класс spider — он определяет, как мы будем извлекать данные из сайтов. Допустим, нам нужно собрать информацию с одностраничного сайта. Создаём класс TestSpider, наследуемся от scrapy.Spider, добавляем атрибут name с уникальным именем и start_urls, где укажем страницы, с которых нужно начать поиск. После этого переопределим метод parse.</p><p>Parse является дефолтным для обработки ответов. Если вы сделаете request и не укажете функцию, которая должна его обработать, то ответ от запроса придёт в метод parse. Поэтому его, как минимум, нужно переопределить и назначить логику.</p><p>Допустим, нас интересует информация о компании — название и описание. Можем создать объект данных с двумя элементами — title и content, и с помощью двух xpath селекторов забрать со страницы заголовок и описание.</p><p>В конце передаём этот объект данных для последующей обработки в ядро. Это всё, что нужно, чтобы начать парсинг на Scrapy.</p><h2>Обработка данных в Scrapy</h2><p>Дальше в ход вступает PipeLine. Он отвечает за обработку данных, валидацию или сохранение. В Scrapy есть пайплайны, готовые из коробки, но вы также можете написать их самостоятельно.</p><p>В ValidatePipeline мы проверяем объект данных на наличие title, а в SavePipeline — сохраняем объект в качестве json.</p><p>Здесь я просто показал, как можно создать пайплайн самостоятельно. Но имейте в виду, что это не очень отказоустойчивый код, поэтому в проде так делать не стоит.</p><h2>Зачем нужен Middleware</h2><p>Middleware очень похож на PipeLine, только он обрабатывает не объекты данных, а запросы и ответы от сайта.</p><p>В Scrapy есть несколько разных middleware:</p><ul><li><b>scheduler middleware:</b> помещает запросы в очередь и извлекает их для обработки.</li><li><b>spider middleware: </b>управляет данными между пауком и ядром.</li><li><b>downloader middleware: </b>управляет данными между ядром и загрузчиком.</li></ul><p>Получается такая схема работы компонентов Scrapy:</p><p><i>Запрос: Spider → Spider Middleware → Engine → Scheduler Middleware → Scheduler → Engine → Downloader Middleware → Downloader → Server (сайт)</i></p><p><i>Ответ: Server → Downloader → Downloader Middleware → Engine → Spider Middleware → Spider → Item Pipeline → Storage (хранилище данных)</i></p><p>Скорее всего, в первую очередь вы будете настраивать downloader middleware, поэтому разберём его подробнее.</p><p>Ниже продемонстрировал два примера, как можно написать свой Middleware. В RandomProxyMiddleware мы обрабатываем все запросы, которые будет отсылать наш паук с помощью метода process_request (добавляем рандомную прокси к каждому реквесту), а в CheckCaptchaMiddleware — обрабатываем все ответы с помощью метода process_response (проверяем ответ от сайта, ищем в нём слово captcha).</p><p>Итак, мы написали Pipeline и Middlewares. Дальше нужно указать, как мы будем их использовать. Для этого запишите их в файле <a href="http://settings.py/">settings.py</a>, где находятся настройки проекта.</p><p>Начнём с пайплайнов. Цифра справа — это порядковый номер выполнения. То есть первым будет ValidatePipeLine, а вторым сработает SavePipeline. Обычно цифры указываются от 0 до 1000. И в случае с пайплайнами чем ниже цифра, тем раньше сработает пайплайн.</p><p>В случае с middleware картина такая же, но логика немного иная.</p><p>Каждый middleware может обрабатывать как request, так и response. При этом middleware стоит посередине между ядром, который отправляет запросы, и загрузчиком. Если ваша middleware обрабатывает запросы, то сработает первой та, что указана с меньшей цифрой, потому что она ближе к ядру. А если middleware обрабатывает ответы, то первой будет та, у которой цифра больше, потому что она дальше от ядра и ближе к загрузчику.</p><figure><img src="https://media.tproger.ru/user-uploads/108715/2025-04-06/db359adf-e3fd-40c8-81e5-5c0d9658bd0a.png" alt="" /></figure><p>Об этот нюанс часто спотыкаются новички, хотя, скорее всего, он прописан в документации.</p><h2>Пример, как использовать Scrapy</h2><p>Рассмотрим, как с помощью одного селектора собрать сайт любой вложенности и архитектуры.</p><p>Для начала создаём класс паука, наследуемся от scrapy.Spider, указываем name, start_urls и атрибут allowed_domains — он необязательный, но в данном случае без него не обойтись. В нём мы укажем список хостов, на которые разрешаем ходить нашему пауку, чтобы он не начал собирать другие сайты.</p><p>Дальше переопределяем метод parse и, когда к нам приходит ответ от сайта, находим все ссылки. И это и есть тот единственный xpath селектор, который поможет обойти весь сайт и собрать страницы.</p><p>Все ссылки помещаем в переменную url в качестве списка, а потом этот список передаём в функцию follow_all объекта response.</p><p>Чтобы дописать сохранение, просто передаём объект response вглубь ядра Scrapy, где напишем какой-то нехитрый пайплайн и будем сохранять html-страницы. Так можно собрать абсолютно любой сайт, просто подставьте ссылку на него в start_urls.</p><p>Важный нюанс: под капотом follow_all сделает запросы к сайту по всем ссылкам, которые мы нашли на странице. И поскольку в follow all мы не указываем определённый метод в параметре callback, то все ответы придут сюда же в метод parse (потому что он дефолтный в Scrapy). Эта логика будет повторяться на каждой странице.</p><p>Ещё в Scrapy есть внутренний фильтр, поэтому если паук соберёт дубликаты, то автоматически зафильтрует их и не будет проходить по ссылкам дважды.</p><h2>Немного итогов</h2><p>Scrapy — это большой, сложный, но очень хороший фреймворк, как Django в веб-разработке. Он предлагает большой выбор готовых инструментов для сбора и обработки данных, а также, поддерживает асинхронное выполнение задач, что ускоряет процесс парсинга.</p><p>Scrapy может показаться трудным для новичков, но у него есть богатая документация и примеры, поэтому при желании в нём нетрудно разобраться.</p>]]></content:encoded>
    </item>
    <item>
      <title>Семь API, которые сократят вам недели разработки</title>
      <link>https://tproger.ru/articles/10-api--kotorye-sokratyat-vam-nedeli-razrabotki</link>
      <comments>https://tproger.ru/articles/10-api--kotorye-sokratyat-vam-nedeli-razrabotki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/10-api--kotorye-sokratyat-vam-nedeli-razrabotki</guid>
      <description><![CDATA[<p>В этом списке — семь мощных API, которые помогут вам ускорить разработку, автоматизировать рутинные задачи и без лишних усилий добавить крутые функции. От баз данных книг до парсинга сайтов и анализа пользовательских данных</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/10-api--kotorye-sokratyat-vam-nedeli-razrabotki">Семь API, которые сократят вам недели разработки</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 04 Apr 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Представьте, что вам нужно встроить в своё приложение поиск книг, анализ геоданных или генерацию случайных пользователей. Вы могли бы писать код с нуля, разбираться с источниками, тестировать и отлаживать… или просто воспользоваться готовыми API, которые сделают всю работу за вас. Сегодня о них и поговорим.</p><h2>Shodan API: Поиск уязвимостей в интернете за минуты</h2><p><a href="https://developer.shodan.io/">Shodan</a> — поисковая система для интернет-устройств. В отличие от Google, который индексирует веб-страницы, Shodan сканирует открытые порты, сервисы и устройства, подключённые к интернету. Это делает его мощным инструментом для исследователей безопасности, разработчиков и системных администраторов.</p><p>Shodan API позволяет автоматизировать поиск уязвимых серверов, камер наблюдения, баз данных и других интернет-ресурсов. Может анализировать их конфигурации и даже отслеживать инциденты безопасности в реальном времени.</p><h2>Как Shodan API экономит время разработчикам?</h2><p>Ручной аудит серверов и интернет-устройств может занять недели, а то и месяцы, но Shodan API позволяет:</p><ul><li>Быстро находить уязвимые устройства и сервисы</li><li>Проверять, какие технологии и версии ПО используются на серверах</li><li>Получать статистику по открытым портам, SSL-сертификатам и угрозам</li><li>Отслеживать новые уязвимости в реальном времени</li></ul><p>Для DevOps, SOC-аналитиков и специалистов по информационной безопасности это возможность автоматизировать рутинные проверки и защитить инфраструктуру от потенциальных атак.</p><h2>Как использовать Shodan API?</h2><p>Shodan API предоставляет удобные методы для работы с данными через REST-запросы. Рассмотрим основные возможности.</p><h4>Поиск открытых сервисов и устройств</h4><p>Shodan позволяет находить устройства, доступные по определённым портам, IP-адресам или географическим координатам. Например, запрос всех открытых баз данных MongoDB:</p><p>Ответ покажет список IP-адресов, страну расположения серверов и используемые версии ПО.</p><h4>Получение информации об IP-адресе</h4><p>Допустим, вы хотите узнать, какие сервисы запущены на конкретном IP. Используем команду:</p><p>Ответ будет содержать список открытых портов, заголовки HTTP-ответов и используемые технологии.</p><h4>Поиск устройств по версии ПО</h4><p>Чтобы найти все серверы с устаревшей версией OpenSSH, можно выполнить запрос:</p><p>Это полезно для поиска серверов, подверженных атакам из-за старых версий ПО.</p><p>Важно понимать, что Shodan не предназначен для хакерских атак. Использование API для несанкционированного сканирования чужих серверов может быть незаконным. Поэтому рекомендуется работать только с теми системами, на которые у вас есть разрешение.</p><h2>Abstract API: Быстрая проверка IP, валидация email и работа с геоданными</h2><p><a href="https://www.abstractapi.com/">Abstract API</a> — сервис, предлагающий набор API для работы с IP-адресами, валидацией email, проверкой телефона, распознаванием валют и многим другим. Это универсальный инструмент для веб-разработчиков, аналитиков и специалистов по безопасности.</p><h3>Как Abstract API экономит время разработчикам?</h3><p>Вместо того чтобы искать разные API для работы с геоданными, email-валидацией и IP-адресами, можно использовать Abstract API. Это экономит часы на интеграцию и позволяет быстро решать задачи, такие как:</p><ul><li>Определение страны и города пользователя по IP</li></ul><ul><li>Проверка подлинности email перед регистрацией</li></ul><ul><li>Конвертация валют в реальном времени</li></ul><ul><li>Валидация телефонных номеров</li></ul><p>Abstract API помогает автоматически фильтровать спам-регистрации, защищать системы от ботов и улучшать пользовательский опыт.</p><h3>Как использовать Abstract API?</h3><p>API работает через REST-запросы и доступно для бесплатного использования с ограничениями.</p><h4>Определение геолокации по IP</h4><p>Можно быстро определить страну, город и провайдера пользователя:</p><h4>Валидация email-адреса</h4><p>Проверяем, является ли email настоящим, одноразовым или корпоративным:</p><p>Что нужно помнить? Бесплатная версия ограничена числом запросов в месяц. А данные по IP-геолокации иногда могут быть неточными (зависит от провайдера).</p><h2>Zyte API: Интеллектуальный ротационный прокси для веб-скрейпинга без блокировок</h2><p><a href="https://www.zyte.com/smart-proxy-manager/">Zyte API</a> — мощный API для веб-скрейпинга, который не только обходит блокировки и капчи, но и автоматически структурирует полученные данные. Он объединяет в себе прокси-серверы, обработку JavaScript-страниц и инструменты парсинга, что делает его одним из самых удобных решений для сбора данных с веб-ресурсов.</p><h2>Как Zyte API экономит время?</h2><p>Вместо того чтобы вручную разрабатывать сложные парсеры и бороться с защитами сайтов, Zyte API позволяет получить уже готовые структурированные данные:</p><ul><li>Автоматическая обработка JavaScript-страниц (открывает динамически загружаемые сайты, как Selenium).</li></ul><ul><li>Обход капч и блокировок (использует интеллектуальные прокси).</li></ul><ul><li>Автоматическое структурирование данных (не просто HTML, а уже готовая JSON-структура).</li></ul><ul><li>Интеграция с Python и REST API (работает с любыми языками программирования).</li></ul><p>API идеально подходит для разработчиков, аналитиков, маркетологов и исследователей данных.</p><h2>Как использовать Zyte API?</h2><p>Он работает как обычный прокси: достаточно настроить его в коде, и все запросы к сайтам будут проходить через интеллектуальную систему ротации IP.</p><h4>Использование Zyte в curl</h4><p>Допустим, нужно скачать HTML-страницу сайта example.com:</p><p>Что нам ответят:</p><h4>Интеграция с Python</h4><p>Для начала нужно установить клиент:</p><p>Код для парсинга и получения данных:</p><h4>Автоматический парсинг данных</h4><p>Zyte API умеет не только загружать HTML, но и автоматически извлекать полезные данные. Например, спарсить цену, название и описание кроссовок в интернет-магазине (ну или любого другого товара).</p><p>Из минусов — нет бесплатного доступа (лишь пробный период). Также некоторые страницы требуют больше времени для обхода ограничений (может понадобиться доп.настройка).</p><h2>Common Crawl API: Бесплатная база данных для веб-скрейпинга и анализа интернета</h2><p><a href="https://commoncrawl.org/">Common Crawl</a> — не просто API, а целый архив интернета, содержащий огромные объемы веб-данных, собранных с 2008 года. В отличие от стандартных API для веб-скрейпинга, Common Crawl предоставляет доступ к готовым копиям страниц, что значительно ускоряет анализ веб-контента и снижает нагрузку на исходные сайты.</p><h3>Как Common Crawl API экономит время?</h3><p>Вместо того чтобы разрабатывать сложные парсеры и загружать миллионы страниц вручную, Common Crawl позволяет быстро находить нужную информацию в готовых архивах:</p><ul><li>Бесплатный доступ к огромной базе веб-страниц (петабайты данных, обновляемых ежемесячно).</li></ul><ul><li>Исторические данные (можно анализировать, как изменялся контент сайтов за годы).</li></ul><ul><li>Отсутствие блокировок и капч (данные уже собраны, вам не нужно бороться с защитами сайтов).</li></ul><ul><li>Возможность массового анализа веба (идеально для NLP, машинного обучения и SEO-исследований).</li></ul><p>API и данные Common Crawl полезны для исследователей, дата-аналитиков, SEO-специалистов и разработчиков.</p><h3>Как использовать Common Crawl API?</h3><p>Common Crawl предоставляет данные в формате WARC (архивные копии страниц) и WET (чистый текст без HTML). Доступ осуществляется через Amazon S3, но также можно использовать API Common Crawl Index для поиска нужных URL.</p><h4>Поиск веб-страниц через API</h4><p>Допустим, нам нужны все страницы, содержащие example.com:</p><p>Вот такой ответ может быть:</p><h4>Получение текста страницы из архива</h4><p>После получения ссылки на WARC-файл можно скачать его и распаковать:</p><p>Ну и затем извлечь текст:</p><h4>Анализ больших объемов данных с AWS</h4><p>Если вам нужны миллионы страниц, можно использовать AWS Athena для обработки данных прямо в облаке.</p><p>Пример SQL-запроса в AWS Athena для поиска страниц с «machine learning»:</p><p>Важно отметить, что данные предоставляются в сыром виде и их нужно дополнительно обрабатывать. Плюс нет гарантии, что конкретная страница будет в архиве.</p><h2>GitHub API: автоматизация работы с репозиториями, пользователями и кодом</h2><p><a href="https://docs.github.com/en/rest">GitHub API</a> — интерфейс для взаимодействия с кодом, репозиториями, пользователями и организациями на платформе GitHub. Он позволяет автоматизировать задачи, получать аналитику, управлять репозиториями, отслеживать запросы на вытягивание, коммиты и многое другое.</p><h3>Как GitHub API экономит время?</h3><p>Вместо ручного управления репозиториями и кодом через интерфейс GitHub можно автоматизировать эти процессы с помощью API:</p><ul><li>Автоматизация деплоя и CI/CD (создание и управление GitHub Actions).</li></ul><ul><li>Мониторинг активности в репозиториях (новые коммиты, запросы на вытягивание, проблемы).</li></ul><ul><li>Управление пользователями и организациями (добавление разработчиков, управление доступом).</li></ul><ul><li>Анализ кода и метрик (подсчёт строк кода, статистика участников).</li></ul><ul><li>Поиск по репозиториям и файлам (быстрое извлечение нужной информации).</li></ul><p>GitHub API полезен для DevOps-инженеров, разработчиков, владельцев проектов и аналитиков.</p><h3>Как использовать GitHub API?</h3><p>GitHub API работает через REST-запросы и возвращает данные в формате JSON. Для авторизации можно использовать токен личного доступа (PAT) или OAuth.</p><h4>Получение информации о пользователе GitHub</h4><p>Допустим, мы хотим узнать данные о пользователе natasharostova:</p><p>Как нам могут ответить:</p><h4>Создание нового репозитория через API</h4><p>После выполнения запроса появится новый репозиторий new-repo.</p><h4>Поиск репозиториев по ключевому слову</h4><p>Допустим, мы хотим найти репозитории, содержащие код на Python, связанный с машинным обучением:</p><p>Нужно понимать, что есть ограничение в 5000 API-запросов в час для авторизованных пользователей. Для некоторых функций требуется версия GitHub Enterprise.</p><h2>MuleSoft API: Универсальный коннектор для интеграции сервисов</h2><p><a href="https://openlibrary.org/developers/api"></a><a href="https://www.mulesoft.com/">MuleSoft API</a> — платформа для интеграции различных систем, сервисов и приложений. Она позволяет соединять облачные и локальные системы, автоматизировать обмен данными и управлять API. MuleSoft широко используется в корпоративных средах для построения сложных интеграционных решений.</p><h3>Как MuleSoft API экономит время?</h3><p>Вместо того чтобы разрабатывать интеграции с нуля, MuleSoft API предлагает готовые коннекторы, которые позволяют:</p><ul><li>Интегрировать разные системы (CRM, ERP, базы данных, облачные сервисы) без сложного кодинга.</li></ul><ul><li>Автоматизировать обмен данными между приложениями (например, между Salesforce и SAP).</li></ul><ul><li>Обеспечивать безопасность API с помощью встроенных инструментов управления доступом.</li></ul><ul><li>Создавать микросервисную архитектуру, где API работают как модули.</li></ul><p>API полезен для DevOps-инженеров, архитекторов ПО, разработчиков корпоративных решений и интеграторов.</p><h3>Как использовать MuleSoft API?</h3><p>MuleSoft поддерживает REST и SOAP API, а также интеграцию через готовые коннекторы.</p><h4>Создание API с помощью Anypoint Platform</h4><p>Anypoint Platform — облачная среда MuleSoft, в которой можно управлять API.</p><p>Пример запроса к API через MuleSoft:</p><h4>Подключение к базе данных через DataWeave</h4><p>DataWeave — это язык MuleSoft для трансформации данных. Он позволяет легко преобразовываться в нужные форматы.</p><p><i>Это правило конвертирует XML-ответ базы данных в JSON.</i></p><p>Важно помнить, что бесплатные возможности платформы ограничены. Также сервис требует обучения: для работы с DataWeave и Anypoint Platform нужно разбираться в интеграции API.</p><h2>JSONPlaceholder API: бесплатный фиктивный REST API для тестирования и создания прототипов</h2><p><a href="https://jsonplaceholder.typicode.com/">JSONPlaceholder API</a> — бесплатный REST API, предназначенный для тестирования, создания прототипов и обучения разработчиков. Он предоставляет фиктивные данные (пользователей, публикаций, комментариев и т. д.), которые можно использовать при разработке клиентских и серверных приложений без необходимости развёртывать собственный бэкенд.</p><h3>Как JSONPlaceholder API экономит время?</h3><p>Разработчикам часто нужно тестировать фронтенд или отлаживать API-запросы, но не всегда есть готовый бэкенд. JSONPlaceholder API решает эту проблему:</p><ul><li>Позволяет мгновенно получать тестовые данные без развертывания сервера.</li></ul><ul><li>Не требует регистрации или API-ключа.</li></ul><ul><li>Поддерживает стандартные HTTP-методы (GET, POST, PUT, DELETE).</li></ul><ul><li>Полностью совместим с популярными библиотеками и фреймворками (Axios, Fetch API, jQuery и др.).</li></ul><p>API полезен для фронтенд-разработчиков, тестировщиков, студентов и преподавателей программирования.</p><h3>Как использовать JSONPlaceholder API?</h3><p>JSONPlaceholder предоставляет несколько ресурсов, которые можно запрашивать с помощью HTTP-запросов.</p><h4>Получение списка пользователей</h4><p>Простейший GET-запрос возвращает список тестовых пользователей:</p><h4>Получение списка постов</h4><p>Можно запросить список фиктивных публикаций:</p><h4>Добавление нового поста</h4><p>Можно отправить POST-запрос, чтобы имитировать создание записи:</p><h4>Обновление записи</h4><p>Для изменения существующей записи можно использовать PUT-запрос:</p><p>Что нужно помнить? Данные статичны — они не сохраняются между запросами. Запросы POST, PUT и DELETE не изменяют реальные данные. А само API предназначено только для тестирования, а не для использования в рабочей среде.</p>]]></content:encoded>
    </item>
    <item>
      <title>Конвейер DevOps, часть 2: пользуемся Fedora Core и mise</title>
      <link>https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-mise</link>
      <comments>https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-mise?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Филон]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-mise</guid>
      <description><![CDATA[<p>В этой серии статей Олег Филон, ментор Эйч Навыки, рассказывает, как прийти к крутому CI/CD пайплайну. Сегодня разбираемся, как выбрать ВМ и настроить облака с помощью Fedora Core и инструмента mise.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/konvejer-devops--chast-2--polzuemsya-fedora-core-i-mise">Конвейер DevOps, часть 2: пользуемся Fedora Core и mise</a>»</p>]]></description>
      <category><![CDATA[C++]]></category>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 31 Mar 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Я — Олег Филон,<a href="https://h.careers/curators/oleg-philon"> ментор Эйч Навыки</a> и Senior DevOps Engineer.<b> </b>В этой статье расскажу, что такое Fedora Core и mise и как с помощью них настроить облака.</p><p><b></b>Дисклеймер! Перед прочтением настоятельно рекомендую ознакомиться с <a href="https://tproger.ru/articles/konvejer-devops--chast-1--kak-organizovat-rabochee-mesto-i-nastroit-oblako-iz-kvm-libvirt">первой частью</a> статьи.</p><p>Если вы знакомы с FreeBSD, вы уже знаете её достоинства и недостатки по сравнению с Linux. Просуммирую некоторые отличия, важные для выбора облачной ВМ.</p><ul><li>FreeBSD не поддерживает контейнеры в привычном понимании — ни docker, ни podman пока не портированы.</li><li>FreeBSD использует свою собственную лицензию <a href="https://www.freebsd.org/copyright/freebsd-license/">The FreeBSD Copyright</a>, позволяющую не раскрывать derivative work. Оценить распространённость FreeBSD иногда затруднительно, возможно, часть её кода используется и в Windows(C), и в MacOS(C). Только иногда разработчики сами признают её использование, например, <a href="https://habr.com/ru/companies/itglobalcom/articles/662415/">Евгений Гаврилов, руководитель проекта vStack</a>.</li><li>Метод разработки FreeBSD ближе к академическому стилю. Есть базовый набор пакетов base, включающий не только ядро, но и достаточно инструментов для сборки остальной системы, именуемой ports. Развитие системы управляется <a href="https://docs.freebsd.org/en/books/faq/#responsible">elected Core Team</a> — сейчас в команде 9 человек.</li><li>Во FreeBSD давно используется система snapshots/rollback, позволяющая откатить изменения, если что-то сломалось.</li></ul><p>Существует дистрибутив Linux, у которого есть некоторые из перечисленных особенностей — это Fedora Core.</p><h2>Что такое Fedora Core и как с ней работать</h2><p>Изначально эти идеи возникли в другом дистрибутиве — RedHat CoreOS.</p><p>Во-первых, система ориентирована на использование в облаках: в ней предоставляются образы для всех возможных систем виртуализации и форматов дисков. Во-вторых, она разделена на базовую систему, часть из которой монтируется только в режиме чтения (ro:read only), и остальное, доступное через контейнеры.</p><p>Обновление системы возможно автоматически, появилась особая утилита rpm-ostree, и можно откатить на предыдущую версию, если что-то не так. Таким образом, CoreOS становится иммутабельной, изменения базовой части системы невозможны в обход коммитов rpm-ostree.</p><p>Я как-то провел эксперимент: установил в Yandex Cloud версию FedoraCore 35, выпущенную в конце 2021 года, с включённым по умолчанию сервисом zincati, который автоматически обновляет базовую систему (у него стабильные релизы). Посчитал, сколько коммитов сделано с тех пор: да, как и в git, здесь используется CAS (content-addressable storage), уже привычные нам хэши. ВМ обновилась и рестартанула 8 раз, если я не пропустил какой-либо рестарт:</p><p>Теперь можно сравнить с этой же системой, установленной из свежего имиджа — как и ожидалось, системы не отличались, хэши коммитов одинаковы. Это то, что имеется ввиду под иммутабельностью ОС. Кроме иммутабельности и смонтированных только по чтению некоторых каталогов, есть и другие меры безопасности. Используется версия Security-Enhanced Linux, никаких логинов по умолчанию, настройки пользователя либо через утилиту cloud-init, либо специальная утилита butane и так называемый ignition file.</p><p>У <a href="https://docs.fedoraproject.org/en-US/docs/">проекта Fedora</a> есть подробнейшая документация — в ней лежит самое авторитетное <a href="https://docs.fedoraproject.org/en-US/fedora-coreos/tutorial-setup/">руководство по установке</a>. Тем не менее у меня возникла пара вопросов:</p><ul><li>Во-первых, для преобразования .yaml файла в ignition, который по сути обычный .json файл, используется особый инструмент butane, написанный на языке Go. Судя по размеру образа 130M, он довольно увесистый, но никак не упоминаются другие утилиты для конвертациии .yaml в .json.</li><li>Во-вторых, в простейшем случае для первого логина вообще не нужны ни butane, ни .yaml. Вот пример минимального ignition.json файла, который создаётся руками:</li></ul><p>Скопируем этот файл в каталог /tmp, он нужен один раз для первого входа, не содержит секретов, к нему нужно будет указать полный путь. Как и ранее, скачиваем свежий образ для libvirt/qemu: <a href="https://fedoraproject.org/coreos/download?stream=stable#arches">выбираем нужную архитектуру и формат диска</a>, проверяем контрольные суммы и цифровые подписи. Команды для создания ВМ будут немного отличаться от уже опробованных из прошлой статьи — сначала сделаем для нашего ignition.json опцию, которую понимает virt-install:</p><p>Затем распаковываем образ в стандартный каталог и создаём ВМ:</p><p>Созданная заранее переменная шелл использована как "${IGN[@]}". virt-install выдаст интереснейший лог процесса инсталяции, а в самом конце — перед приглашением — покажет IP-адрес вновь созданной ВМ. Переходим в другую консоль и проверяем наш ssh-ключ: ssh -v core@192.168.100.2 в моём случае. После успешного логина выполняем первоочерёдные проверки и настройки:</p><p>Теперь ВМ можно остановить и сделать финальные настройки нашего персонального облака для этой ВМ. Добавим MAC- и статический IP-адреса в dnsmasq.conf и зададим имя новой ВМ в сети (пример есть в прошлой статье). Откроем в virt-manager окно overview, затем откроем вкладку XML и в самом конце удалим конфиг для qemu — целиком вот этот фрагмент:</p><p>Далее кнопка Apply. Возможно, virt-manager попросит сначала установить Edit → Preferences → General → Enable XML editing. То же самое можно сделать из командной строки: sudo virsh edit fc41stab. Ignition-файл нужен только для установки, далее эту ВМ можно донастраивать, клонировать, копировать её образ куда угодно, в том числе в большие облака.</p><p>А мы продолжим знакомство с этой необычной системой, как её рекламирует проект Федора:</p><p>Fedora CoreOS — это автоматически обновляемая, минимальная, монолитная, ориентированная на контейнеры операционная система, разработанная для кластеров, работоспособная автономно, оптимизированная для Kubernetes.</p><p>Проверим, что dnsmasq обновился, и начнём по порядку.</p><p>Далее выполняем команды внутри ВМ. Основное, к чему придётся привыкнуть, — что часть каталогов смонтированы readonly, запись в них в обход утилиты rpm-ostree невозможна даже судоерам:</p><p>Я специально грепнул только реальные устройства, потому что кроме них смонтированы ещё 19 виртуальных — пока углубляться не будем. Как видно, в режиме rw смонтированы /etc и /var. Даже стандартый home — это линк /home → var/home.</p><p>За автоматические обновления отвечает сервис zincati. Если его отключить, обновления можно запускать вручную: rpm-ostree --bypass-driver upgrade. После рестарта проверяем новое состояние системы:</p><p>rpm-ostree rollback позволяет откатить обновления к предыдущему комиту. Другие полезные команды:</p><ol><li>rpm-ostree db diff — список обновлённых пакетов между комитами</li><li>rpm-ostree db list — список и версии пакетов в комитах</li></ol><p>А как же устанавливать пакеты привычным dnf? Это своевременный вопрос. Посмотрим сначала что уже стоит:</p><p>Это напоминает FreeBSD base, но важно, что набор включает podman, docker, git — для остального есть утилита toolbox. Она делает доступными все пакеты федоры, упакованные в контейнер. Заглянем в toolbox:</p><p>Мы оказались внутри контейнера — об этом говорит изменившееся приглашение $PS1. Если снова посмотреть mount, увидим ещё больше хитро подмонтированных каталогов — этакий оверлей из базовой и контейнерной ФС. Но зато работает dnf, поэтому можно устанавливать любые пакеты. Добавим привычные, без них неуютно в новой системе:</p><p>Как видим, пакеты доступны, но желательно иметь их вне контейнера. Нет проблем: скопируем их в свой ~/bin каталог, он одинаков и внутри, и снаружи контейнера:</p><p>Выйдем из контейнера и проверим:</p><h2>А теперь серьезно: настраиваем Fedora Core сервер для команды разрабов</h2><p>Для начала настроим полноценный dev env: репозиторий кода, образы контейнеров, инструменты для сборки и сервер приложений.</p><p>Обычно в команде есть один админ или несколько. Уже созданный пользователь core — это админ, у него есть права sudo, плюс ему доступен контейнер tbox. В нашей Федоре каталог с секретами устроен особым образом:</p><p>Первоначально обычный файл authorized_keys назывался ignition и располагался в каталоге ~/.ssh/authorized_keys.d, что подразумевает, что можно ещё добавить ключей, назвав их никами других админов. Так же можно поступить с разрабами — настроить для всех один dev env.</p><p>Создадим нового пользователя без прав sudo:</p><p>Как мы уже знаем, мелкие утилиты можно просто скопировать из тулбокса в ~/bin или /usr/local/bin. Для языков программирования и прочих инструментов есть замечательная программа <a href="https://mise.jdx.dev/">mise</a>. Сейчас мы её установим для dev и немного познакомимся. Свой ключ я уже добавил в ~dev/.ssh/authorized_keys.d/, так что логинимся и настраиваем инструменты для команды разрабов.</p><p>mise позволяет устанавливать и использовать различные версии языков программирования, инструментов и библиотек. Другие менеджеры версий, например, <a href="https://github.com/aquaproj/aqua">aqua</a>, <a href="https://github.com/asdf-vm/asdf">asdf</a>, <a href="https://github.com/houseabsolute/ubi">ubi</a> — собрали репозитории таких инструментов, и mise их использует в качестве <a href="https://mise.jdx.dev/dev-tools/backends/">бэкендов</a>. Огласим весь список:</p><p>Да, список великоват: сохранил в файл — 883 штук, и он постоянно растёт. Это только названия пакетов, поиск версий, а команда mise ls-remote python выдала 911 вариантов для python.</p><p>Зато теперь можно настраивать пайплайны для наших разработчиков. А в следующей статье разберемся, как работать с пайплайнами и хуками в git.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как перевести проект на Laravel: пошаговый план перехода</title>
      <link>https://tproger.ru/articles/kak-perevesti-proekt-na-laravel--powagovyj-plan-perehoda</link>
      <comments>https://tproger.ru/articles/kak-perevesti-proekt-na-laravel--powagovyj-plan-perehoda?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-perevesti-proekt-na-laravel--powagovyj-plan-perehoda</guid>
      <description><![CDATA[<p>Как перевести проект на Laravel. Показываем основные преимущества использования Ларавел. Рассматриваем пошаговую инструкцию нюансы переноса ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-perevesti-proekt-na-laravel--powagovyj-plan-perehoda">Как перевести проект на Laravel: пошаговый план перехода</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Laravel]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[DPI]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 30 Mar 2025 09:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда код устаревает, его поддержка превращается в борьбу с хаосом. После пересмотра архитектуры приходит пугающее осознание: нужно переезжать на более современный стек. Однако непонятно, с чего начать.</p><p><i>В этой статье пошагово разберем перенос вашего проекта на Laravel. Вы узнаете, как мигрировать с минимальными рисками для разработки и пользователей.</i></p><h2>Шаг 1. Готовимся к миграции</h2><p>Посмотрите структуру проекта, чтобы выявить слабые места.</p><p>Архитектура далека от идеала, если проект вырос на хаотичных добавлениях новых функций.</p><p>Проанализируйте:</p><ul><li><b>Вид проекта</b>. Ваша разработка — это монолит, модульная система, API, микросервис?</li><li><b>Зависимости</b>. Определите, какие библиотеки использует проект. Совместимы ли они с Laravel?</li><li><b>Базу данных</b>. Если в БД встречаются дубли, отсутствуют ограничения, это нужно исправить заранее.</li></ul><p>В качестве теста на совместимость перепишите небольшую часть на Laravel. Возьмите один простой модуль и посмотрите, насколько это жизнеспособно.</p><h2>Стратегии перехода</h2><p>Выберем стратегию перехода:</p><ul><li>Big Bang,</li><li>Поэтапный перенос.</li></ul><p><b>Стратегия Big Bang</b> — это полная остановка текущего проекта и создание новой версии на Ларавел с нуля.</p><p>Подход идеален для устаревших и запутанных систем.</p><p>Из минусов — высокие риски. Во время разработки текущая версия может растерять пользователей. К тому же сроки реализации могут затянуться.</p><p><b>Стратегия с постепенным переходом</b> выглядит надежнее.</p><p>Система продолжает работать, а вы поэтапно меняете ее «под капотом».</p><p>Единственный нюанс — готовьтесь к сложностям поддержки, когда часть системы заработает в новой архитектуре, а часть останется в старой.</p><h4>Выбор подхода зависит от проекта</h4><p>Если систему легко поддерживать, то более безопасен постепенный переход.</p><p>Проект напоминает спагетти-код? Плохие новости — Big Bang неизбежен.</p><h2>Шаг 2. Разворачиваем новое окружение на Laravel</h2><p>Чтобы установить фреймворк, выполните команду:</p><p>Прежде чем работать с кодом, настроим окружение.</p><p>В корне проекта для этого есть файл конфигурации .env:</p><ul><li><b>APP_NAME</b>: имя приложения, чтобы оно отображалось в логах и заголовках.</li><li><b>APP_URL</b>: адрес, на котором приложение будет доступно (например, http://localhost).</li></ul><p>В будущем запланирована интеграция с существующей базой, поэтому лучше сразу настроить подключение.</p><h3>Подключение БД</h3><p>Laravel использует <b>Eloquent ORM</b> для взаимодействия с базой.</p><p>Настройка БД выполняется в том же разделе .env:</p><p>Для тестирования подключения запустите команду:</p><h2>Настройка маршрутов</h2><p>Маршруты изначально хранятся в файлах <i>routes/web.php</i> (для страниц) или <i>routes/api.php</i> (для <a href="https://tproger.ru/translations/luchshie-praktiki-razrabotki-rest-api-20-sovetov">REST API</a>).</p><p>В качестве примера добавим базовый маршрут приветствия:</p><p>Если у вас проект с десятками пользовательских маршрутов, интегрируйте их постепенно.</p><h2>Шаг 3. Переносим модели данных и бизнес-логики</h2><p>Eloquent ORM работает с классами, представляющими таблицы в базе данных.</p><p>Чтобы перенести существующую таблицу, нужно создать <b>модель</b>:</p><p>Этот класс автоматически подключается к таблице с именем products.</p><p>Когда название таблицы не соответствует модели, это можно указать явно:</p><p>Если в таблице есть нестандартные поля (например, столбцы с именами вроде created_time вместо created_at), их тоже можно легко обработать:</p><h2>Переносим схему БД</h2><p>Laravel использует механизм <b>миграций </b>для управления таблицами в базе и <b>сиды </b>для внесения данных.</p><p>Перенос структуры БД выполняется командой:</p><p>Миграция создается в папке <i>database/migrations</i>.</p><h3>Переносим данные</h3><p>Если текущая БД уже содержит данные, их можно перенести через <b>сиды</b>: файлы, которые заполняют таблицы данными.</p><p>Создаем сид:</p><p>Внутри метода run описываем данные:</p><h3>Переводим бизнес-логику в сервисы и фасады</h3><p>Старый проект состоит из функционала, размазанного по контроллерам? Улучшим структуру и сделаем код независимым. С этим помогут сервисы и фасады.</p><p><b>Сервисы </b>— это классы, которые объединяют бизнес-логику в одном месте.</p><p>Например, если проект выполняет расчеты цен и скидок, можно создать класс:</p><p>В этом классе описана бизнес-логика:</p><p>Теперь сервис можно вызывать в контроллерах и моделях.</p><p><b>Фасады </b>— это статический интерфейс к сервисам, который делает вызовы более читаемыми.</p><p>Например:</p><p>С фасадом вызов становится простым:</p><h2>Шаг 4. Перенос маршрутов и контроллеров</h2><p>Начнем с <b>маршрутов</b>. Они определяют, как пользователи будут взаимодействовать с приложением.</p><p>Если в старом проекте использовались API-запросы для работы с внешними сервисами или мобильными приложениями, перенесем их в <i>api.php</i> и добавим префикс api для маршрутов:</p><p>Теперь про <b>контроллеры</b>. Они отвечают за логику обработки запросов и взаимодействие с моделями. В Laravel контроллеры работают по правилам <b>MVC </b>(Model-View-Controller).</p><p>Пример обычного метода контроллера:</p><p>Laravel определяет маршруты групповыми методами — по префиксам, middleware или зонам авторизации.</p><p><b>Middleware </b>— это посредники, которые обрабатывают запросы до их поступления в приложение.</p><p>Навесим middleware на маршруты, требующие авторизации пользователя:</p><p><b>Политики </b>контролируют доступ к определенным ресурсам, например, правку или удаление записей.</p><p>Пример метода политики:</p><p>Политики можно подключать к маршрутам или напрямую вызывать в контроллерах.</p><h2>Шаг 5. Перенос фронтенда и представлений</h2><p>Если ваш старый проект использует обычные HTML-файлы, они легко преобразуются в Blade.</p><p><b>Blade </b>— это встроенный механизм шаблонов. С его помощью создают пользовательские интерфейсы.</p><p>Например, перенесем главную страницу сайта:</p><p>Blade-версия:</p><p>Теперь можно динамически передавать данные из контроллера:</p><p>Blade поддерживает механизм наследования, который упрощает работу с повторяющимися частями интерфейса.</p><h3>Интегрируем Vue.js или React</h3><p>Vue.js/React подключают, когда нужен динамичный и интерактивный интерфейс. Например, для <a href="https://tproger.ru/articles/ssr-ili-spa-veb-sajty-chto-vybrat-dlya-vas-i-vawego-biznesa">SPA</a>, реалтайм-функционала, сложных форм или фильтров.</p><p>Blade и JavaScript можно комбинировать: используйте Blade для рендеринга начального интерфейса и Vue.js/React для обработки интерактива.</p><h3>Динамический интерфейс без JS</h3><p><b>Livewire </b>— это инструмент для создания интерфейсов без необходимости обращаться к внешним библиотекам JavaScript. Преимущество в том, что весь код пишется на PHP.</p><p>Livewire пригодится для динамических форм, фильтрации таблиц, уведомлений. Он упрощает код, сохраняя гибкость.</p><h2>Шаг 6. Оптимизируем и тестируем проект</h2><p>Отправная точка улучшения производительности — работа с Blade-шаблонами. Laravel сам их оптимизирует, но есть практики по дополнительному ускорению:</p><ul><li><b>Минимизация логики в представлении</b>. Злоупотребление PHP внутри Blade замедляет рендеринг страниц. Вместо описания сложной логики в шаблонах ее лучше перенести в контроллер.</li><li><b>Кэширование шаблонов</b>. Работу приложения можно ускорить за счет запуска кэширования всех шаблонов, сокращая время компиляции — «<i>php artisan view:cache</i>».</li><li><b>Переиспользование</b>. Если фронтенд состоит из повторяющихся фрагментов, рекомендуется использовать Blade-компоненты.</li></ul><p>В приложениях SPA оптимизируем фрагменты JavaScript.</p><p><b>Laravel Mix</b> генерирует минифицированные CSS и JS. Проверьте размер этих файлов в папке <i>public/js</i>. Если их вес больше 1 МБ:</p><ul><li>Удалите лишние пакеты.</li><li>Используйте динамическую загрузку маршрутов или компонентов.</li></ul><p>Для Vue можно использовать динамическую загрузку, чтобы не грузить весь фронтенд сразу:</p><h3>Тестирование</h3><p>Убедимся, что приложение работает корректно.</p><p>Проверим, например, что страница приветствия открывается и показывает нужный текст:</p><p>Если используете Vue.js или React, для проверки компонентов применяйте <b>Jest </b>или <b>Mocha</b>.</p><p>Тест, который проверяет, что компонент отобразил нужный текст:</p><p>Если приложение работает с API:</p><p>Для проверки приложения под нагрузкой можно использовать <b>Artillery </b>или <b>JMeter</b>.</p><h2>Шаг 7. Разворачиваем и запускаем проект</h2><p>Перенесли данные, создали маршруты и контроллеры, внедрили представления, оптимизировали приложение и протестировали его. Остался последний шаг — развернуть проект на сервере и запустить его.</p><p>Выбор сервера зависит от предпочтений и нагрузок:</p><ul><li>Если важна скорость и масштабируемость, выбирайте <a href="https://tproger.ru/articles/video-osnovy-nginx-dlja-nachinajushhih-za-200-sekund">Nginx</a>. Он быстрее работает под высокой нагрузкой.</li><li>Если важен простой подход, используйте <a href="https://tproger.ru/video/video-osnovy-apache-kafka">Apache</a>. Он интегрируется с большинством систем.</li></ul><p>Фоновые задачи можно связать с <b>Supervisor</b>. Инструмент автоматически запускает воркеров при сбоях, обеспечивая бесперебойную обработку задач.</p><h3>Автоматизация развертывания</h3><p>Чтобы релиз прошел безболезненно, нужен рабочий процесс CI/CD. Например, в <a href="https://tproger.ru/articles/integraciya-ci-cd-processov-s-ispolzovaniem-github-actions">GitHub Actions</a> можно настроить сценарий, который:</p><ol><li>Проверит качество кода.</li><li>Установит зависимости.</li><li>Выполнит миграции.</li><li>Зальет проект на сервер.</li></ol><p>Предусмотрите откат, например, через <b>Spatie Laravel Backup</b>. Если что-то пойдет не так, у вас должна быть возможность вернуть предыдущую версию.</p><p>Инструмент <b>Sentry </b>поможет отслеживать ошибки, а <b>New Relic</b> — оценивать производительность.</p><h2>Заключение</h2><p>Устаревшие проекты уникальны, к каждому нужно искать индивидуальный подход. Надеемся, что пошаговое руководство станет основой для миграции вашего legacy-приложения на Laravel без переписывания кода с нуля.</p>]]></content:encoded>
    </item>
    <item>
      <title>Энтузиаст рассказал, как запустил Go на PlayStation 2 — через TinyGo, ps2dev и боль</title>
      <link>https://tproger.ru/news/entuziast-rasskazal--kak-zapustil-go-na-playstation-2---cherez-tinygo--ps2dev-i-bol</link>
      <comments>https://tproger.ru/news/entuziast-rasskazal--kak-zapustil-go-na-playstation-2---cherez-tinygo--ps2dev-i-bol?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/entuziast-rasskazal--kak-zapustil-go-na-playstation-2---cherez-tinygo--ps2dev-i-bol</guid>
      <description><![CDATA[<p>Go запустили на PlayStation 2: энтузиаст адаптировал TinyGo и ps2dev для MIPS-процессора консоли и обошёл ограничения LLVMa</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/entuziast-rasskazal--kak-zapustil-go-na-playstation-2---cherez-tinygo--ps2dev-i-bol">Энтузиаст рассказал, как запустил Go на PlayStation 2 — через TinyGo, ps2dev и боль</a>»</p>]]></description>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[PlayStation]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 28 Mar 2025 08:22:54 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчик под псевдонимом Ricardo <a href="https://rgsilva.com/blog/ps2-go-part-1/">опубликовал</a> подробный отчёт о том, как ему удалось запустить Go-программу на PlayStation 2 — консоли 2000 года.</p><p>Он использовал компилятор TinyGo, SDK ps2dev и собственные костыли, чтобы адаптировать современный язык программирования под устаревшую архитектуру.</p><h2>TinyGo, MIPS и странности платформы</h2><p>PS2 построена на процессоре MIPS R5900 (архитектура MIPS-III с расширениями), который не поддерживается стандартной сборкой Go. Проблему частично решает TinyGo, который компилирует Go-код в LLVM IR.</p><p>Но даже с ним возникли сложности: ни LLVM, ни сам TinyGo не умеют работать с особенностями PS2 «из коробки». Пришлось вручную описывать платформу через ps2.json, определять baremetal-окружение, runtime и заглушки под прерывания.</p><h2>Первые успехи: «Hello from Go»</h2><p>Чтобы код TinyGo можно было линковать с библиотеками из ps2dev (а они скомпилированы под ABI N32), автору пришлось добиваться совместимости: целиться в MIPS-III, использовать hard-float, отключать abicalls и собирать IR с последующей ручной сборкой объектника через Clang с нужными флагами.</p><p>После успешного линка и запуска в эмуляторе PCSX2 программа вывела строку и число на отладочный экран PS2.</p><h2>Собственный main() и отладка</h2><p>Дальше автор отказался от загрузчика на C и написал полноценный main() на Go, вручную выделяя память под кучу и завершая программу через exit() из ps2dev. Он добавил простую обёртку над scr_printf() — теперь можно печатать текст прямо из Go.</p><h2>DDIVU и проклятие деления</h2><p>Серьёзным багом стал сбой fmt.Sprintf: PS2 не поддерживает инструкцию DDIVU, которую LLVM генерирует при делении uint64.</p><p>Автор обошёл это, подключив функции __udivdi3, __divdi3 и прочие, а затем пропатчил компилятор TinyGo, чтобы он использовал их при делении int64 и uint64.</p><h2>Что дальше</h2><p>Демо работает. Go-код запускается напрямую, выводит текст и корректно делит числа.</p><p>Впереди — системные вызовы, inline-ассемблер, поддержка прерываний и, возможно, новая цель в LLVM для процессора r5900.</p>]]></content:encoded>
    </item>
    <item>
      <title>15 вопросов с собеседований по фронтенду для мидлов</title>
      <link>https://tproger.ru/articles/15-voprosov-s-sobesedovanij-po-frontendu-dlya-midlov</link>
      <comments>https://tproger.ru/articles/15-voprosov-s-sobesedovanij-po-frontendu-dlya-midlov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Анна Ельцова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/15-voprosov-s-sobesedovanij-po-frontendu-dlya-midlov</guid>
      <description><![CDATA[<p>Пов: вы пришли на собес на мидла. В статье — ищите вопросы и пытайтесь ответить сначала сами. Если не получается — ответы и решения тоже есть.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/15-voprosov-s-sobesedovanij-po-frontendu-dlya-midlov">15 вопросов с собеседований по фронтенду для мидлов</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Собеседование]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Safari]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 26 Mar 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Статей в духе «вопросы для фронтенд разработчика написано много». Опыт Альберта Халимова в прохождении и проведении собеседований показывает, что, задавая базовые вопросы, интервьюер хочет узнать, готовился ли человек и насколько он заинтересован в работе.</p><p>На типичные вопросы Junior-разработчик должен легко ответить. Вместе с <a href="https://h.careers/curators/albert-khalimov">Альбертом Халимовым</a>, TeamLead Frontend Developer в Группе М.Видео-Эльдорадо и ментором <a href="https://h.careers/skills?utm_source=site_tproger&amp;utm_medium=article">Эйч Навыки</a>, разбираемся в тех же вопросах, но смотрим на них под другим углом и узнаем самое главное — готовы ли быть мидлом.</p><h2>1. Что такое оператор typeof?</h2><p>Вызов typeof x возвращает строку с именем типа:</p><h2>2.     Что вернет typeof null, typeof new Date, typeof alert?</h2><p>Date — это встроенный объект, который содержит дату и время, а также предоставляет методы управления ими.</p><p>Результатом вызова typeof null является "object". Это официально признанная ошибка в typeof, берущая начало еще со времен создания JavaScript и сохраненная для совместимости. Конечно, null — не объект. Это специальное значение с отдельным типом.</p><p>Вызов typeof alert возвращает "function", потому что alert — функция. Функции относятся к объектному типу. Но typeof обрабатывает их особым образом, возвращая "function". Так тоже повелось с момента создания JavaScript. Формально это неверно, но может быть удобным на практике.</p><p>Интересный факт: информация об операторе typeof — это последний пункт главы «Типы данных» на <a href="https://learn.javascript.ru/">https://learn.javascript.ru/</a>.</p><h2>3. Что такое NaN? Какого типа это значение? Как можно узнать, равно ли значение переменной NaN?</h2><p>NaN расшифровывается как "Not A Number", это "false" (ложное) значение. Будьте аккуратны: выражение typeof NaN возвращает тип Number. Чтобы проверить значение переменной на соответствие NaN, нужно воспользоваться встроенным методом isNaN() или оператором тройного равенства ===.</p><h2>4. Какие есть способы объявления функций? В чем их отличия? Что такое стрелочная функция?</h2><p>Ниже рассмотрим ключевые отличия Function Declaration от Function Expression.</p><p>Во-первых, синтаксис: Function Declaration — функция объявляется отдельной конструкцией function… в основном потоке кода.</p><p>Function Expression — функция, созданная внутри другого выражения или синтаксической конструкции. В данном случае функция создается в правой части «выражения присваивания» =:</p><p>Более тонкое отличие появляется тогда, когда функция создаётся движком JavaScript. Так, Function Expression появляется в то время, как выполнение доходит до него, и затем уже может использоваться.</p><p>После того, как поток выполнения достигнет правой части выражения присваивания let sum = function…, функция считается созданной и может быть использована (присвоена переменной, вызвана и т.д. ).</p><p>С Function Declaration всё иначе. Она может быть вызвана раньше, чем объявлена. Другими словами, когда движок JavaScript готовится выполнять скрипт или блок кода, прежде всего, он ищет в нём Function Declaration и создаёт все такие функции. Можно считать этот процесс «стадией инициализации».</p><p>Только после того, как все объявления Function Declaration будут обработаны, продолжится выполнение. В результате функции, созданные как Function Declaration, могут быть вызваны раньше своих определений.</p><p>Например, будет работать так:</p><p>Функция sayHi была создана, когда движок JavaScript подготавливал скрипт к выполнению, и такая функция видна повсюду в этом скрипте.</p><p>Если бы это было Function Expression, то такой код вызвал бы ошибку:</p><p>Функции, объявленные при помощи Function Expression, создаются тогда, когда выполнение доходит до них. Это случится только на строке, помеченной звёздочкой (*). Слишком поздно.</p><p>Ещё одна важная особенность Function Declaration заключается в её блочной области видимости.</p><p>В строгом режиме, когда Function Declaration находится в блоке {...}, функция доступна везде внутри блока, но не снаружи.</p><p>Для примера давайте представим, что нам нужно объявить функцию welcome() в зависимости от значения переменной age, которое мы получим во время выполнения кода. И затем запланируем использовать её когда-нибудь в будущем.</p><p>Если мы попробуем использовать Function Declaration, это не заработает так, как задумывалось:</p><p>Это произошло, так как объявление Function Declaration видно только внутри блока кода, в котором располагается.</p><p>Вот ещё один пример:</p><p>Что можно сделать, чтобы welcome была видна снаружи if? Верным подходом будет воспользоваться функцией, объявленной при помощи Function Expression, и присвоить значение welcome переменной, объявленной снаружи if, что обеспечит нам нужную видимость.</p><p>Такой код заработает, как ожидалось:</p><p>Или мы могли бы упростить это ещё сильнее, используя условный оператор:</p><p>Так когда использовать Function Declaration, а когда Function Expression?</p><p>Как правило, если нам понадобилась функция, нужно рассматривать синтаксис Function Declaration, который мы использовали до этого. Он даёт больше свободы в том, как мы можем организовывать код. Функции, объявленные таким образом, можно вызывать до их объявления.</p><p>Также функции вида function f(…) {…} чуть более заметны в коде, чем let f = function(…) {…}. Function Declaration легче «ловятся глазами».</p><p>…Но если Function Declaration нам не подходит по какой-то причине или нам нужно условное объявление (мы рассмотрели это в примере выше), то следует использовать Function Expression.</p><h2>5. Что такое функции высшего порядка? Приведите примеры.</h2><p>Функции высшего порядка — это функции, которые могут:</p><ol><li>Принимать функции в качестве аргументов.</li><li>Возвращать функции как результат своей работы.</li></ol><p>Вот встроенные функции высшего порядка в JavaScript:</p><ul><li>map() — применяет функцию ко всем элементам массива.</li><li>filter() — фильтрует элементы массива на основе условия.</li><li>reduce() — сводит массив к одному значению, применяя функцию.</li></ul><h2>6. Что такое Promise и какие бывают состояния?</h2><p>Promise — объект в JavaScript, который представляет результат асинхронной операции. Промис позволяет обрабатывать результат операции, когда он станет доступным, вместо того, чтобы блокировать выполнение кода и ожидать завершения операции.</p><p>Промис может находиться в одном из трех состояний:</p><ul><li><b>Pending</b> — исходное состояние промиса. Он находится в ожидании выполнения или отклонения операции.</li><li><b>Fulfilled</b> — промис переходит в это состояние, когда операция успешно завершается.</li><li><b>Rejected</b> — промис переходит в это состояние, когда операция завершается с ошибкой. Здесь промис возвращает причину ошибки.</li></ul><p>Пример:</p><h2>7.     Как сравнивать в JS? Как сравнить два массива?</h2><p>Когда вы сравниваете примитивные типы (числа, строки, булевы значения и т.д.), используются операторы == (нестрогое сравнение) и === (строгое сравнение).</p><p>==  проверяет равенство значений после приведения типов.</p><p>=== проверяет как равенство значений, так и типов (без приведения типов).</p><p>При сравнении массивов в JavaScript важно помнить, что массивы — это объекты, и сравнение происходит по ссылке. То есть два массива будут равны только в том случае, если они указывают на одну и ту же ссылку в памяти.</p><p>Можно преобразовать массивы в строки JSON и сравнить их. Это удобный способ, но он имеет несколько ограничений (например, порядок элементов в массиве имеет значение).</p><p>Для более точного сравнения массивов (в том числе с вложенными структурами) можно пройтись по каждому элементу массива с помощью цикла for или методов every/some.</p><h2>8.     Чем отличаются any, unknown и never?</h2><p>Основные различия:</p><ul><li>any: можно присваивать любые значения, без проверки типа.</li><li>unknown: можно присваивать любые значения, но необходимо проверять тип перед использованием.</li><li>never: тип для значений, которые не могут существовать, например, функции, которые не возвращают ничего.</li></ul><h2>9.     Что такое CORS и как его можно решить на фронтенде?</h2><p>CORS — механизм безопасности, который предотвращает доступ к данным с других источников. Чтобы решить проблемы с CORS:</p><ul><li>На фронтенде можно использовать прокси-серверы для разработки.</li><li>В продакшн-окружении необходимо настроить CORS на сервере.</li><li>Если вы не контролируете сервер, то решением будет обращение к разработчикам API или использование серверного прокси для обхода ограничений.</li></ul><h2>10.  Что такое Progressive Web Apps (PWA)? Зачем нужно?</h2><p>Progressive Web Apps (PWA) — тип веб-приложений, который использует современные веб-технологии для предоставления пользователям опыта, схожего с нативными мобильными приложениями, но через обычный веб-браузер. PWA можно использовать в любом браузере, который поддерживает стандарты веба. Такие приложения могут работать оффлайн, присылать пуши, быстро загружаться и подстраиваться под любой браузер, который поддерживает эти фичи. Из примеров — банковские веб-приложения, которые можно установить на экран.</p><p>Вот главные особенности PWA:</p><ul><li><b>Работа в оффлайн-режиме (Offline-first). </b>PWA используют Service Workers, которые позволяют веб-приложению работать даже без интернета. Это значит, что приложение может кешировать данные и ресурсы, обеспечивая доступность контента в оффлайн-режиме или при плохом интернет-соединении.</li><li><b>Установка на устройство (Installable).</b> PWA можно устанавливать на устройства как нативные приложения. При этом скачивать их из магазина пользователю не нужно.</li><li><b>Push-уведомления.</b> PWA могут отправлять push-уведомления — так пользователь может получать информацию, даже если приложение закрыто.</li><li><b>Обновления.</b> Приложения могут автоматически обновляться в фоновом режиме.</li><li><b>Мгновенная загрузка.</b> PWA загружаются быстрее, так как они используют кэширование ресурсов и данных через Service Workers — это улучшает производительность даже при медленном интернете.</li><li><b>Универсальность.</b> PWA работают на всех платформах (мобильные устройства, десктопы, планшеты) и в любых браузерах, которые поддерживают современные веб-стандарты (например, Chrome, Firefox, Safari, Edge и т.д.).</li></ul><h2>11.  Lifecycle во Vue: когда использовать хуки жизненного цикла?</h2><p>В Vue 2 жизненный цикл выглядит следующим образом:</p><p>beforeCreate → created → beforeMount → mounted → beforeUpdate → updated → beforeDestroy → Destroyed</p><p>В Vue 3 жизненный цикл был немного изменен и улучшен. Некоторые хуки переименованы:</p><p>beforeDestroy → beforeUnmount</p><p>destroyed → unmounted</p><p>Кроме того, в Vue 3 появилась возможность использовать Composition API, который предоставляет более гибкий способ организации жизненного цикла компонентов. Теперь можно создавать компоненты с помощью функции setup().</p><p>Когда использовать хуки жизненного цикла?</p><ul><li>beforeCreate и created используются для настройки данных, свойств или методов компонента до и после его создания.</li><li>beforeMount и mounted полезны для операций, которые необходимо выполнить при рендере компонента (например, вызов API, настройка сторонних библиотек, или выполнение начальной логики).</li><li>beforeUpdate и updated можно использовать для наблюдения за изменениями данных или для манипуляций с DOM до или после его обновления.</li><li>beforeDestroy и destroyed (или beforeUnmount и unmounted в Vue 3) полезны для очистки ресурсов перед уничтожением компонента.</li></ul><h2>12.  В каком компоненте отработает mounted вначале — дочернем или родительском?</h2><p>В Vue.js хук mounted вызывается для компонента, как только его DOM вставляется в дерево DOM. Когда компонент — часть иерархии (например, родительский и дочерний компоненты), хуки жизненного цикла выполняются в определенном порядке.</p><p>Хук mounted будет вызван для дочернего компонента перед родительским.</p><p>Объяснение: родительский компонент инициирует рендеринг и монтирование дочернего компонента. Когда Vue монтирует компоненты, он сначала монтирует все дочерние компоненты, а потом родительский компонент. Это связано с тем, что родительский компонент ждет завершения монтирования дочерних компонентов, чтобы их правильно вставить в DOM.</p><h2>13.  Как изменить commit message</h2><h4>1. Изменить последний коммит</h4><p>Если нужно изменить сообщение только последнего коммита, используйте команду:</p><p>git commit --amend</p><p>После этого Git откроет редактор, где вы сможете изменить сообщение коммита. Сохраните изменения и выйдите из редактора.</p><h4>2. Изменить старый коммит</h4><p>Если нужно изменить сообщение не последнего коммита, а более старого, можно воспользоваться rebase:</p><p>git rebase -i &lt;commit_id&gt;</p><p>Здесь &lt;commit_id&gt; — идентификатор коммита, сообщение которого вы хотите изменить. Можно использовать git log для получения ID нужного коммита. В открывшемся файле замените слово pick на reword напротив коммита, который хотите изменить, и сохраните файл. Git откроет редактор для изменения сообщения этого коммита. После редактирования сохраните и выйдите.</p><p>Завершите процесс rebase:</p><p>git rebase --continue</p><h4>3. Изменить сообщение в уже отправленном коммите (Push)</h4><p>Если коммит был уже отправлен в удаленный репозиторий, а вы хотите изменить сообщение коммита, то вам нужно будет форсировать пуш.</p><p>Измените коммит с помощью git commit --amend или git rebase -i.</p><p>После изменения выполните принудительный пуш:</p><p>git push --force</p><p>Пожалуйста, перед собеседованием повторите CSS. Многие разработчики усиленно готовятся к технической сессии по JS, TS, своему фреймворку, но забывают о верстке. Если вам повезет работать над новым продуктом, то верстка будет занимать больше половины времени.</p><h2>14.  Что такое псевдоклассы в CSS? Как обратиться к псевдоклассу в JS?</h2><p>Псевдоклассы в CSS — стили, которые применяются  к элементам в зависимости от их состояния или структуры в документе.</p><p>В JavaScript нельзя напрямую изменить псевдоклассы, но можно использовать селекторы CSS для выбора элементов с псевдоклассами. Это делается с помощью методов по типу querySelector и querySelectorAll.</p><h2>15.  Что такое dvh, lvh, em и rem в CSS? Когда и почему лучше использовать каждую из этих единиц измерения?</h2><p>Эти единицы были введены в спецификации CSS, чтобы улучшить работу с адаптивным дизайном и обеспечить корректное отображение на мобильных устройствах.</p><ul><li>dvh (dynamic viewport height) — единица измерения, которая учитывает динамические изменения в размерах окна, например, высота при открытии клавиатуры на мобильных устройствах.</li><li>lvh (large viewport height) — единица измерения, которая использует максимальную высоту вьюпорта, независимо от динамических изменений (например, не учитывает изменения, связанные с клавиатурой на мобильных устройствах).</li></ul><p>Когда использовать:</p><ul><li>dvh полезен, когда нужно учитывать изменения размера окна при взаимодействии с мобильными устройствами (например, при открытии клавиатуры).</li><li>lvh полезен для работы с мобильными устройствами, когда нужно обеспечить стабильный дизайн, не зависящий от изменения высоты вьюпорта из-за динамических изменений.</li><li>em — это единица измерения, которая зависит от размера шрифта родительского элемента. 1em равен текущему размеру шрифта родительского элемента. Если в родительском элементе установлен шрифт размером 16px, то 1em будет равен 16px.</li><li>rem — это единица измерения, которая зависит от размера шрифта корневого элемента (&lt;html&gt;). 1rem равен размеру шрифта, установленному на корневом элементе (по умолчанию в большинстве браузеров это 16px).</li></ul><p>Когда и зачем использовать каждую из этих единиц?</p><ul><li>dvh/lvh: Используйте, если вам нужно учитывать динамическое изменение высоты вьюпорта (например, на мобильных устройствах при открытии клавиатуры) или если вам нужно работать с максимальной высотой вьюпорта, независимо от динамических изменений.</li><li>em: Используйте, когда размеры элементов должны зависеть от размера шрифта родительского элемента. Это полезно для создания гибких и адаптивных макетов, которые изменяются с размером шрифта.</li><li>rem: Используйте, когда нужно, чтобы размеры были относительно размера шрифта корневого элемента. Так проще поддерживать единообразие и легко масштабировать весь интерфейс.</li></ul><p>Если вы знали ответы на все вопросы, поздравляем — можете гордо считать себя мидлом и смело ходить на собеседования. Если в каких-то вопросах плаваете, то подучите теорию и порешайте побольше задачек/почитайте код других разработчиков — тогда все получится.</p>]]></content:encoded>
    </item>
    <item>
      <title>Context Collapse: как микросервисы могут сойти с ума</title>
      <link>https://tproger.ru/articles/context-collapse--kak-mikroservisy-mogut-sojti-s-uma</link>
      <comments>https://tproger.ru/articles/context-collapse--kak-mikroservisy-mogut-sojti-s-uma?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Анна Ельцова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/context-collapse--kak-mikroservisy-mogut-sojti-s-uma</guid>
      <description><![CDATA[<p>Даже самая идеальная микросервисная архитектура может упасть. В статье обсудим зарубежный материал, где автор рассказывает о проблеме Context Collapse.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/context-collapse--kak-mikroservisy-mogut-sojti-s-uma">Context Collapse: как микросервисы могут сойти с ума</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Docker]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[OpenTelemetry]]></category>
      <category><![CDATA[Grafana]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 25 Mar 2025 12:40:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В феврале на Medium вышла статья <a href="https://levelup.gitconnected.com/context-collapse-the-silent-microservices-killer-76a60058561e">Context Collapse: The Silent Microservices Killer</a>. Перевели ее для вас, так как тема довольно редкая и интересная. Ниже предлагаем обсудить проблему контекста и как он может упасть.</p><h2>Что вообще такое context collapse</h2><p>Есть вероятность, что вы слышите этот термин в первый раз. Немного лирики: в мире микросервисов есть два понятия: технический контекст и доменный контекст (Bounded Context).</p><h2>Технический контекст</h2><p>Технический контекст — это информация, которая передается между микросервисами для выполнения запросов или операций. В нем лежит большое количество данных: идентификаторы запросов (например, TraceID в трассировке), пользовательские сессии, метаданные или параметры аутентификации.</p><p>Без технического контекста микросервисы не могут:</p><ul><li>отслеживать, откуда пришел запрос и куда он направляется,</li><li>сохранять согласованность данных между сервисами,</li><li>выявлять сбои (например, через логи или трассировку).</li></ul><h2>Доменный контекст (Bounded Context)</h2><p>Доменный контекст происходит из методологии Domain-Driven Design (DDD) — это про четкое разделение бизнес-логики между микросервисами. У каждого сервиса должна быть своя «ограниченная область» (Bounded Context), где термины, данные и правила имеют уникальное значение.</p><p>Например, в интернет-магазине слово «заказ» может означать разные вещи для сервиса оплаты (финансовая транзакция) и сервиса доставки (отправка покупателю). Если границы контекста не определены четко, возникает путаница: сервисы начинают дублировать логику или интерпретировать данные по-разному.</p><p>Без доменного контекста:</p><ul><li>код будет дублироваться и появится избыточность,</li><li>усложнится взаимодействие между сервисами,</li><li>систему будет сложнее поддерживать.</li></ul><h2>Вернемся к коллапсу контекста</h2><p>Автор статьи просит нас представить следующую картину: вы сделали новую архитектуру, она масштабируется, разделяется, деплои проходят гладко, CI/CD пайплайны цветут и пахнут — другими словами, не архитектура, а мечта. Все работает как часы, поэтому вы сидите, попивая кофе и раскладывая пасьянс.</p><p>Вдруг приходит пользователь и сообщает, что платеж был проведен дважды. На панели мониторинга появляются задержки, и логи здесь вообще бесполезны. Вы начинаете разбираться и спустя несколько часов споров с терминалом, находите причину: один из сервисов забыл, кто вообще такой этот пользователь, прямо во время проведения транзакции.</p><p>Тра-та-та, это и есть контекстный коллапс. Как называет его автор — тихий убийца микросервисов в 2025 году. Поймать этот баг с помощью breakpoints нельзя, при этом он будет уничтожать вашу производительность и код в целом.</p><h2>Что на самом деле происходит</h2><p>Представьте, что микросервисы — это эстафетный забег: каждый сервис передает «эстафетную палочку» — идентификаторы пользователей, сессионные данные, намерения — следующему участнику. Контекстный коллапс случается, когда эта палочка выпадает на бегу.</p><p>Сервис теряет важное состояние, например:</p><ul><li>«Это корзина Пети»</li><li>"Этот платеж уже прошел"</li></ul><p>И начинается хаос: дублирующиеся API-запросы, потерянные транзакции или, что еще хуже, повреждение данных.</p><p>В 2025 году появляется все больше слишком фрагментированных архитектур и гипермасштабируемых систем. Ваши запросы могут проходить через 10, 20 и даже 30 сервисов, но если на каком-то этапе отвалился контекст, то это конец.</p><p>В статье автор не делает акцента на том, какой именно это контекст — технический или доменный. На самом деле может произойти все что угодно: это может быть как потеря технического контекста, так и нарушение доменных границ (когда сервисы лезут не в свое дело). Оба случая приводят к хаосу: данные становятся несогласованными, ошибки множатся, а разработчики теряют контроль над системой.</p><h2>Как понять, что контекст вот-вот может «коллапснуться»</h2><p>Вы наверняка были в такой ситуации: вы на 100% уверены, что система должна работать, но она не работает, хотя ошибок в коде никаких, казалось бы, нет. Вот основные признаки коллапса, с которыми вы можете столкнуться:</p><ul><li>Всплеск пинга: сервисы запрашивают данные, которые должны бы уже знать, создавая огромное количество лишних вызовов и перегружая API.</li><li>Дублирующиеся действия: повторные списания, задвоенные отправки писем, неожиданные повторные заказы. Пользователи возмущены, рейтинг падает.</li><li>Мистические баги: логи говорят «всё в порядке», но результат явно неверный. Код не видит проблему, а отладка превращается в кошмар.</li></ul><p>Давайте рассмотрим на примере. Клиент решает перевести деньги с одного счёта на другой. Сервис транзакций отправляет запрос в модуль проверки лимитов, затем передаёт его в систему обработки платежей. Но из-за потери контекста на одном из этапов модуль проверки лимитов теряет информацию о сессии клиента и воспринимает его как нового пользователя. В результате:</p><ul><li>Лимиты не распознаются, и транзакция блокируется, даже если у пользователя достаточно денег.</li><li>Либо, наоборот, проверка пропускается, и клиенту позволяют перевести больше лимита.</li><li>Сервис обработки платежей не получает подтверждение проверки и отправляет повторный запрос, а это может привести к двойному списанию средств.</li></ul><p>Клиент идет громить службу поддержки, она — разработчиков, а они размахивают руками, потому что в логах все отлично.</p><p>Опрос CNCF за 2024 год показал, что 68% команд, которые используют микросервисы, сталкиваются с необъяснимыми проблемами производительности. И всему виной в том числе контекстный коллапс.</p><h2>5 решений, как бороться с контекстным коллапсом</h2><p>Да, контекстный коллапс — крайне неприятный баг. Главное — чтобы все сервисы работали слаженно и «знали» друг друга.</p><h3>1. Правильно передавайте контекст</h3><p>Вам нужно передавать критические важные данные — идентификаторы пользователей, состояние транзакций, метаданные запроса — в каждом шаге цепочки. Стоит использовать OpenTelemetry, чтобы распространять контекст по сервисам.</p><p>Вот пример реализации middleware автором на Go:</p><h3>2. Кэшируйте с умом</h3><p>Нет смысла заставлять сервисы угадывать, если они просто могут запомнить. Быстрый кэш, например, с Redis, может хранить временный контекст — например, токены сеанса или состояния запросов, — поэтому сервисы не будут постоянно спрашивать «кто это?» Вот фрагмент кода на Питоне, где используется Redis для хранения и извлечения контекста:</p><p>Это позволяет поддерживать контекст во всех вызовах, не перегружая вашу базу данных. А еще TTL (time-to-live) Redis сам за собой убирает — устаревшие данные не скрываются.</p><h3>3. Используйте Event-Driven Architecture</h3><p>Микросервисы без сохранения состояния выглядят отлично, пока не контекст не коллапснется. Событийная архитектура идет от обратного: вместо того чтобы надеяться, что службы все запомнят, регистрируйте каждый шаг как событие. Если служба все-таки забудет, воспроизведите поток. Вот пример на Node.js с Kafka:</p><p>Здесь также можно использовать RabbitMQ. В общем, больше никаких отговорок со стороны сервиса из разряда «я забыл».</p><h3>4. Проверяйте логи</h3><p>Когда контекст теряется, логи — ваше место преступления. Настройте Grafana Loki или Datadog для поиска «потерянных контекстов». Вот пример с Grafana:</p><p>Тэгните логи с помощью request_id или user_id, а затем вызовите Loki:</p><h3>5. Тестируйте на коллапсы</h3><p>Профилактика лучше, чем лечение. Добавьте хаос-тестирование с помощью, например, Chaos Mesh, чтобы моделировать потери контекста.</p><p>Хаос-тестирование — это метод преднамеренного введения сбоев в систему, чтобы проверить, насколько она устойчива и надежна. Обычное тестирование и мониторинг могут выявить проблемы, но хаос-тестирование (Chaos Engineering) помогает увидеть, как система поведет себя в случае неожиданных отказов.</p><p>Прервите mid-requests к сервисам — сможет ли система восстановится в таком случае? Если нет, значит, ваша передача контекста недостаточно надежна.</p><p>Эти решения не универсальны. Начните с правильного распространения контекста для быстрых улучшений, затем добавьте кэширование или событийную архитектуру для масштабируемости и постоянно аудируйте систему.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработчик собрал топ ошибок в REST API: что бесит всех и как это исправить</title>
      <link>https://tproger.ru/news/--razrabotchik-sobral-top-owibok-v-rest-api--chto-besit-vseh-i-kak-eto-ispravit</link>
      <comments>https://tproger.ru/news/--razrabotchik-sobral-top-owibok-v-rest-api--chto-besit-vseh-i-kak-eto-ispravit?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--razrabotchik-sobral-top-owibok-v-rest-api--chto-besit-vseh-i-kak-eto-ispravit</guid>
      <description><![CDATA[<p>Разработчик собрал топ ошибок в REST API, которые раздражают всех: как их избежать и сделать удобный API без проблем с безопасностью и UX</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--razrabotchik-sobral-top-owibok-v-rest-api--chto-besit-vseh-i-kak-eto-ispravit">Разработчик собрал топ ошибок в REST API: что бесит всех и как это исправить</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 20 Mar 2025 14:08:15 GMT</pubDate>
      <content:encoded><![CDATA[<p>По данным Postman, <b>70% разработчиков</b> сталкиваются с неудобными API, которые мешают работе.</p><p>Многие из них спроектированы с типичными ошибками. Один из разработчиков <a href="https://zuplo.com/blog/2025/03/12/common-pitfalls-in-restful-api-design">составил</a> список самых раздражающих проблем и дал советы, как их избежать.</p><h2>Ошибка №1: API, завязанный на внутренней структуре</h2><p>Проблема: API <b>отражает внутреннюю логику</b>, заставляя клиентов разбираться в БД.</p><p>⛔ GET /api/database/tables/book_inventory/records?status=1</p><p>✅ GET /api/books?available=true</p><p>API должен быть <b>понятным пользователю</b>, а не разработчику системы.</p><h2>Ошибка №2: Кривые URL и HTTP-методы</h2><p>Некоторые API добавляют г<b>лаголы в пути</b> или усложняют структуру.</p><p>⛔ POST /api/deleteCustomer/456</p><p>✅ DELETE /api/customers/456</p><p>Также не стоит делать <b>глубокую вложенность</b>. Например:</p><p>⛔ GET /api/companies/456/departments/2/employees/123/projects</p><p>✅ GET /api/projects?employeeId=123</p><h2>Ошибка №3: Игнорирование обработки ошибок</h2><p>⛔ Самый раздражающий ответ:</p><p>✅ Как правильно:</p><p>Четкие коды ошибок экономят время и нервы разработчиков.</p><h2>Ошибка №4: Отсутствие версионирования</h2><p>Если API обновляется без версионирования, <b>старые клиенты ломаются</b>. Пример правильного подхода:</p><p>GET /api/v1/users</p><p>Accept: application/vnd.company.api+json;version=1</p><p>Важно не удалять старые версии без предупреждения.</p><h2>Ошибка №5: Слишком много данных в ответах</h2><p>Распространенная проблема — это когда API отдает всю информацию, даже если она не нужна.</p><p>Куда правильнее будет позволять клиенту выбирать поля (fields=id,name,email) и Делить данные на отдельные запросы (GET /api/users/123/orders).</p><h2>Ошибка №6: Проблемы с безопасностью</h2><p>Одной из самых частых ошибок с точки зрения безопасности является передача токенов в URL (?token=123). Также начинающие разработчики реализуют API, в которых отсутствуют ограничения запросов (Rate Limit) или используется HTTP вместо HTTPS.</p><p>Исправить это достаточно просто. Нужно использовать OAuth2/JWT и ограничивать частоту (HTTP 429).</p><h2>Ошибка №7: Плохая документация</h2><p>Вместе с тем важно понимать, что без нормальной документации, API никто не будет использовать.</p><p>Наиболее частые проблемы по этой части:</p><ul><li>Нет примеров кода</li><li>Непонятно, как авторизоваться</li><li>Ошибки не объясняются</li></ul><p>Хороший API — это не только код, но и удобство его использования. Не делайте так, чтобы разработчики вас ненавидели.</p>]]></content:encoded>
    </item>
    <item>
      <title>Топ 15 расширений Google Chrome для аналитиков данных</title>
      <link>https://tproger.ru/articles/top-15-raswirenij-google-chrome-dlya-analitikov-dannyh</link>
      <comments>https://tproger.ru/articles/top-15-raswirenij-google-chrome-dlya-analitikov-dannyh?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/top-15-raswirenij-google-chrome-dlya-analitikov-dannyh</guid>
      <description><![CDATA[<p>Узнайте о 15 расширениях Google Chrome для аналитиков данных. Парсинг, визуализация, автоматизация и удобные инструменты для работы с данными — полный обзор с примерами использования.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/top-15-raswirenij-google-chrome-dlya-analitikov-dannyh">Топ 15 расширений Google Chrome для аналитиков данных</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Парсинг]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Google Chrome]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Визуализация]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Google Analytics]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 02 Mar 2025 09:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Работа аналитика данных включает парсинг информации, анализ пользовательского поведения, работу с визуализацией и оптимизацию процессов. Google Chrome предлагает множество расширений, которые помогают решать эти задачи быстрее и эффективнее.</p><p>Мы собрали <b>15</b> <b>инструментов</b>, которые пригодятся аналитикам данных, и разобрали их функционал.</p><h2>Data Scraper —  извлечение данных с веб-страниц</h2><p><a href="https://chromewebstore.google.com/detail/data-scraper-easy-web-scr/nndknepjnldbdbepjfgmncbggmopgden">Ссылка на расширение </a></p><p><b>Для чего?</b> Автоматический сбор таблиц, списков и других данных с сайтов. Он загружает данные в CSV, Excel или Google Sheets без необходимости программирования.</p><p><b>Пример использования.</b> Вы анализируете цены конкурентов в интернет-магазинах и хотите собрать данные о стоимости товаров. Data Scraper автоматически извлекает нужные элементы и экспортирует их в таблицу.</p><p>✅ Плюсы: удобство, простота настройки, экспорт в популярные форматы.</p><p>❌ Минусы: ограниченная бесплатная версия, не работает на защищенных сайтах.</p><h2>Web Scraper — мощный инструмент для автоматизированного сбора данных</h2><p><a href="https://chromewebstore.google.com/detail/web-scraper/dogiinkejekngjnphjklkdohanocpnfj?hl=ru">Ссылка на расширение</a></p><p><b>Для чего?</b> Глубокий парсинг с поддержкой вложенных страниц. Инструмент настраивает сценарии обхода динамических сайтов и сбора их данных.</p><p><b>Пример использования. </b>Вы анализируете вакансии на сайтах работодателей и хотите собрать данные по зарплатам, названиям должностей и требованиям.</p><p>✅ Плюсы: гибкость, автоматический обход страниц.</p><p>❌ Минусы: требует понимания HTML-структуры сайтов.</p><h2>Awesome Table —  превращение данных в интерактивные отчёты</h2><p><a href="https://workspace.google.com/marketplace/app/awesome_table/56088344336">Ссылка на расширение</a></p><p><b>Для чего?</b> Помощь в визуализации данных в Google Sheets. Создает наглядные дашборды, которые можно встроить в сайт или презентацию.</p><p><b>Пример использования.</b> У вас есть список клиентов с продажами, и вы хотите создать интерактивный отчет с фильтрацией по странам, категориям и датам.</p><p>✅ Плюсы: легкость настройки, хорошая интеграция с Google.</p><p>❌ Минусы: ограничения в бесплатной версии.</p><h2>Octoparse —  мощный инструмент для парсинга данных</h2><p><a href="https://chromewebstore.google.com/detail/octoparse-voc-ai-review-r/dcejniggmfiedegekbcindccneeegeoi">Ссылка на расширение</a></p><p><b>Для чего?</b> Автоматизирует сбор данных с веб-страниц без программирования. Позволяет извлекать таблицы, списки товаров, контактные данные, и даже имитировать действия пользователя на сайте.</p><p><b>Пример использования.</b> Допустим, вам нужно собрать список всех статей по определённой тематике с крупного новостного портала. Octoparse создаст сценарий, который будет автоматически переходить по страницам, извлекать заголовки, даты публикации и ссылки, а затем экспортировать данные в CSV или Excel.</p><p>✅ Плюсы: Не требует знаний программирования; поддерживает сложные сценарии парсинга (например, клик по кнопке «Показать ещё»).</p><p>❌ Минусы: Ограниченный функционал в бесплатной версии; может не работать с сайтами, защищёнными от ботов (например, Cloudflare)</p><h2>Selenium IDE — автоматизация тестирования прямо в браузере</h2><p><a href="https://chromewebstore.google.com/detail/selenium-ide/mooikfkahbdckldjjndioackbalphokd">Ссылка на расширение</a></p><p>Для чего? Записывает и выполняет тесты веб-приложений без написания кода. Позволяет тестировать формы, кнопки, навигацию и другие элементы сайтов.</p><p>Пример использования. Вы разработчик или аналитик, которому нужно проверить, как работает новый функционал на сайте. Вместо ручного тестирования Selenium IDE записывает ваши действия (ввод в форму, клик по кнопке) и воспроизводит их автоматически.</p><p>✅ Плюсы: Возможность экспорта сценариев в разные языки (Python, Java, JavaScript).</p><p>❌ Минусы: Ограниченные возможности по сравнению с полноценным Selenium WebDriver.</p><h2>Google Analytics Debugger —  отладка событий в GA</h2><p><a href="https://chromewebstore.google.com/detail/google-analytics-debugger/jnkmfdileelhofjcijamephohjechhna">Ссылка на расширение</a></p><p><b>Для чего?</b> Проверка корректности передачи данных в Google Analytics. Показывает, какие события отправляются в аналитику.</p><p><b>Пример использования.</b> Если в аналитике не отображаются клики по кнопке «Купить», Debugger поможет понять, передаются ли данные в систему.</p><p>✅ Плюсы: подробные логи, быстрая диагностика проблем.</p><p>❌ Минусы: поддерживает только Universal Analytics, не GA4.</p><h2>DataLayer Checker —  проверка передачи данных</h2><p><a href="https://chromewebstore.google.com/detail/datalayer-checker/ffljdddodmkedhkcjhpmdajhjdbkogke">Ссылка на расширение</a></p><p><b>Для чего? </b>диагностика переменных DataLayer в GTM. Показывает, какие данные передаются на сайт.</p><p><b>Пример использования.</b> Хотите убедиться, что в аналитику передаются все параметры заказа? DataLayer Checker покажет их в реальном времени.</p><p>✅ Плюсы: помогает в настройке e-commerce аналитики.</p><p>❌ Минусы: сложен для новичков.</p><h2>Tableau Chrome Extension —  удобная работа с дашбордами</h2><p><a href="https://chromewebstore.google.com/detail/tableau-chrome-extension/ocnnlomigllkiegmgkbodenbgpahbogc">Ссылка на расширение</a></p><p><b>Для чего? </b>Облегчает доступ к дашбордам Tableau. Загружает отчеты без необходимости заходить на сайт.</p><p><b>Пример использования.</b> Вы работаете в команде аналитиков и хотите быстро проверять дашборды Tableau прямо из браузера.</p><p>✅ Плюсы: ускоряет доступ к данным.</p><p>❌ Минусы: требует подписки на Tableau.</p><h2>Block Yourself from Analytics — исключение собственного трафика</h2><p><a href="https://chromewebstore.google.com/detail/block-yourself-from-analy/fadgflmigmogfionelcpalhohefbnehm">Ссылка на расширение</a></p><p><b>Для чего? </b>Чтобы не учитывать свои визиты в Google Analytics.</p><p><b>Пример использования. </b>Вы администратор сайта и не хотите искажать данные в GA своими посещениями.</p><p>✅ Плюсы: легко включается и выключается.</p><p>❌ Минусы: работает только в Chrome.</p><h2>Table Capture —  копирование таблиц с сайтов</h2><p><a href="https://chromewebstore.google.com/detail/table-capture/iebpjdmgckacbodjpijphcplhebcmeop">Ссылка на расширение</a></p><p><b>Для чего?</b> Экспорт таблиц с сайтов в Excel и Google Sheets.</p><p><b>Пример использования. </b>Вам нужно быстро перенести данные текстовой таблицы с веб-страницы в Google Sheets.</p><p>✅ Плюсы: это, как минимум, удобно.</p><p>❌ Минусы: не всегда корректно распознает сложные таблицы.</p><h2>JSONView —  удобное представление JSON-данных</h2><p><a href="https://chromewebstore.google.com/detail/jsonview/gmegofmjomhknnokphhckolhcffdaihd?hl=ru">Ссылка на расширение</a></p><p><b>Для чего? </b>Форматирование JSON-файлов в читаемый вид.</p><p><b>Пример использования.</b> Вы работаете с API и хотите быстро понять структуру JSON-ответов.</p><p>✅ Плюсы: цветовая подсветка.</p><p>❌ Минусы: не редактирует JSON.</p><h2>Scraper —  быстрый экспорт данных с сайтов</h2><p><a href="https://chromewebstore.google.com/detail/scraper/mbigbapnjcgaffohmbkdlecaccepngjd">Ссылка на расширение</a></p><p><b>Для чего? </b>Извлекает текстовые и табличные данные с веб-страниц без сложных настроек.</p><p><b>Пример использования. </b>Вы анализируете список товаров на маркетплейсе и хотите собрать данные о ценах и наличии.</p><p>✅ Плюсы: прост в использовании, есть интеграция с Google Sheets.</p><p>❌ Минусы: ограниченный функционал по сравнению с Web Scraper.</p><h2>Wappalyzer — определение технологий сайтов</h2><p><a href="https://chromewebstore.google.com/detail/wappalyzer-technology-pro/gppongmhjkpfnbhagpmjfkannfbllamg">Ссылка на расширение</a></p><p><b>Для чего?</b> Показывает, какие CMS, языки программирования, аналитические сервисы и рекламные сети использует сайт. Полезен для конкурентного анализа и технических исследований.</p><p><b>Пример использования.</b> Вы изучаете сайты конкурентов и хотите понять, какие технологии они применяют (используют ли Google Tag Manager и т.д.).</p><p>✅ Плюсы: удобный интерфейс, быстрый анализ.</p><p>❌ Минусы: иногда пропускает редкие технологии.</p><h2>OpenLink Structured Data Sniffer — анализ структурированных данных</h2><p><a href="https://chromewebstore.google.com/detail/openlink-structured-data/egdaiaihbdoiibopledjahjaihbmjhdj?hl=en">Ссылка на расширение</a></p><p><b>Для чего?</b> Извлекает и анализирует JSON-LD, RDF на сайтах. Помогает проверить корректность разметки страниц для поисковых систем.</p><p><b>Пример использования.</b> Вы работаете с SEO и хотите проверить, правильно ли настроены schema.org-разметки на сайте клиента.</p><p>✅ Плюсы: детализированный анализ.</p><p>❌ Минусы: требует знаний о структурированных данных.</p><h2>Tag Assistant — анализ работы тегов Google</h2><p><a href="https://chromewebstore.google.com/detail/tag-assistant/kejbdjndbnbjgmefkgdddjlbokphdefk">Ссылка на расширение</a></p><p><b>Для чего?</b> Проверка корректности работы Google Tag Manager, Analytics и Ads. Анализирует теги, выявляет ошибки в их настройке.</p><p><b>Пример использования.</b> Вы настраиваете рекламную кампанию и хотите убедиться, что все теги срабатывают правильно.</p><p>✅ Плюсы: удобный интерфейс, анализ сразу всех тегов Google.</p><p>❌ Минусы: требует знаний о работе тегов.</p><p>А какими расширениями пользуетесь вы? Делитесь в комментариях!</p>]]></content:encoded>
    </item>
    <item>
      <title>Что такое cookies, localStorage и sessionStorage: главные отличия и примеры</title>
      <link>https://tproger.ru/articles/chto-takoe-cookies--localstorage-i-sessionstorage</link>
      <comments>https://tproger.ru/articles/chto-takoe-cookies--localstorage-i-sessionstorage?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/chto-takoe-cookies--localstorage-i-sessionstorage</guid>
      <description><![CDATA[<p>Что такое cookies, localStorage и sessionStorage. Показываем основные отличия и примеры кода. Рассматриваем пошаговую инструкцию и важные особенности каждого вида ✔ Tproger
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/chto-takoe-cookies--localstorage-i-sessionstorage">Что такое cookies, localStorage и sessionStorage: главные отличия и примеры</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Google]]></category>
      <category><![CDATA[Браузеры]]></category>
      <category><![CDATA[Node.js]]></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>Tue, 25 Feb 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Работа современных сайтов и приложений неизбежно связана с сохранением данных на стороне клиента. Эти данные применяются в других частях веб-ресурсов или используются в следующих сессиях. Например, на сайте онлайн-магазина пользователь сохраняет товары в корзине и завершает сессию. Когда он возвращается на сайт, он может продолжить покупку, не потеряв свои товары.</p><p>Как это реализуется на практике? Есть несколько способов работы с информацией. Наиболее востребованные из них — это cookies, localStorage и sessionStorage. Эти инструменты отличаются друг от друга, но далеко не все разработчики могут объяснить разницу между ними.</p><p>Узнаем, что собой представляют cookies, localStorage и sessionStorage, каковы их основные характеристики и особенности, как они применяются и какой способ работы с данными лучше.</p><h2>Cookies</h2><p>На заре интернета cookies были безальтернативным вариантом хранения данных на стороне клиента. Файлы куки представляют собой элементарные пары типа «ключ-значение» и используются для:</p><ul><li>управления текущими сессиями;</li><li>персонализации пользователя;</li><li>отслеживания его активности.</li></ul><p>При этом данные не просто остаются в хранилище в браузере, но и передаются на серверы в виде HTTP-заголовков, выступая частью спецификации протокола. Cookies поддерживают все браузеры — это универсальный способ работы с информацией. И хотя объем данных для хранения ограничен, производительность этого способа — крайне важный показатель для работы современных веб-ресурсов.</p><p>По сути куки — это ограниченные по размеру текстовые файлы, которые сайт сохраняет на устройстве пользователя. Объем данных в «печеньках» лимитирован — это не больше 4 КБ, при этом у них есть срок хранения, который устанавливает разработчик ресурса.</p><p>Например, в Google есть файлы аналитики для сбора информации, которые хранятся от 6 до 24 месяцев. Некоторые cookies действуют только в период работы в браузере и при закрытии автоматически уничтожаются, другие хранятся в течение всего перерыва между сессиями — их срок жизни зависит от выполняемых задач.</p><p>Куки почти идеальный инструмент для идентификации посетителя, но сам пользователь может отключить их почти на любом ресурсе, что делает «печеньки» недостаточно надежным хранилищем информации.</p><figure><img src="https://media.tproger.ru/user-uploads/101078/2025-02-25/444a9012-6723-4a77-9166-14e4b8210d2f.png" alt="" /></figure><p>Развитие этого инструмента продолжается. Например, в экосистеме Chromium доступна версия, которая поддерживает общую память и асинхронный API CookieStore — им можно пользоваться без блокировки главного потока.</p><p>Закрепим основные характеристики cookies:</p><ul><li>сохраняют данные, которые передаются на сервер с помощью заголовков;</li><li>локальные и сессионные куки-хранилища существуют только на стороне клиента;</li><li>сроки хранения устанавливаются заранее при создании куков;</li><li>объем ограничен (4 КБ);</li><li>бывают защищенные куки, но в этом случае их содержимое будет недоступным на клиентской стороне — такой формат используется для аутентификации пользователей при хранении их токенов.</li></ul><p>Способ хранения данных в cookies имеет определенные ограничения. Чтобы сохранить корректность запроса, содержимое должно быть закодированным и безопасным. При этом интерфейс для чтения и записи cookies не всегда удобный, что вынуждает разработчиков работать с ним не напрямую, а через сторонние библиотеки.</p><p>Этот способ применяют, чтобы сохранить данные авторизации либо когда доступ к сохраненным данным требуется на сервере. Кроме того, куки применяют компании, чтобы отслеживать поведение пользователей, но сами браузеры активно этому противодействуют.</p><p>Разработчики на JavaScript используют свойство document.cookie, которое предоставляет строковый формат для работы всех cookies, связанных с текущим процессом. Пример кода:</p><p>В этом примере для установки cookie разработчик присваивает строку свойству document.cookie. Можно задать дополнительные свойства — например, срок действия, путь или домен.</p><p>У файлов cookie довольно широкая область применения — хранение настроек юзера, удержание посетителей сайта, хранение сведений о текущем сеансе, показ целевой рекламы. При этом файлы устанавливает конкретный сервер, и только он может их прочитать.</p><p>Разработчики устанавливают и читают cookie через заголовки Set-Cookie и Cookie непосредственно в протоколе HTTP. В приложениях куки устанавливают и считывают с помощью соответствующего API либо модуля файлов cookie на одном из серверных языков — например, Node.js.</p><p>Среди рядовых юзеров интернета бытует мнение, что куки вредны и опасны. Это не совсем верно. Сами по себе эти файлы не представляют угрозы для данных — они не содержат вирусы и не могут установить на устройство вредоносное ПО. Но если злоумышленники получат доступ к cookies на вашем устройстве, они будут знать все о сеансах конкретного посетителя и могут использовать эти сведения в своих целях.</p><p>Существует риск, что при работе на компьютерах с публичным вай-фаем или при подключении через чужие взломанные устройства, личные данные могут быть похищены. Определенные уязвимости, связанные с cookie, могут возникать на сайтах онлайн-магазинов, которые уделили недостаточно внимания информационной безопасности.</p><p>Чтобы минимизировать риски, юзеру при посещении незнакомых сайтов или при использовании публичных сетей лучше отказаться от автоматического приема куки-файлов. Альтернативный вариант — входить инкогнито.</p><h2>LocalStorage</h2><p>Это более продвинутый формат хранилища в браузере, позволяющий сайтам сохранять больше информации без ущерба для трафика. LocalStorage создали в качестве альтернативы cookie, чтобы обойти ограничения последних. А именно — небольшой объем хранилища, необходимость постоянно передавать данные с сервера в браузер и обратно. В localStorage работа с данными стала проще и эффективнее.</p><p>В локальном хранилище, в отличие от сеансового, данные сохраняются после закрытия и повторного открытия страницы. Файлы работают до тех пор, пока пользователь их не очистит или это не сделает код на JavaScript.</p><p>Информация хранится в формате «ключ-значение» в виде строк. Даже если ввести числовые или логические данные, они преобразуются в строчные. Для использования хранилища в JavaScript используют объект localStorage.</p><figure><img src="https://media.tproger.ru/user-uploads/101078/2025-02-25/07284145-9813-4c0b-aa55-b68172e7de1c.png" alt="" /></figure><p>Пример кода:</p><p>Запись напоминает код для сеансового хранилища. Метод setItem() используется для хранения данных, метод getItem() — для их получения, removeItem() — для удаления конкретных элементов, clear() — для полной очистки данных. Несмотря на то, что в localStorage сохраняются только строки, с помощью JSON разработчики могут взаимодействовать и с более сложными типами данных.</p><p>Основные характеристики localStorage:</p><ul><li>данные хранятся бессрочно;</li><li>очистить хранилище можно только принудительно — через код JavaScript или полную очистку браузерного кэша пользователем;</li><li>объем данных для хранения — 5-10 МБ (по этому показателю localStorage лидирует среди всех хранилищ);</li><li>старыми браузерами не поддерживается — например, на IE7 это локальный ящик не работает;</li><li>действует по принципу ограничения домена — сохраненная информация доступна лишь одному источнику.</li></ul><p>Поскольку данные не исчезают даже после закрытия компьютера, сайты на длительный срок сохраняют нужную им информацию — в первую очередь это предпочтения пользователя и его настройки.</p><p>Эффективность хранилища в том, что данные не нужно отправлять на сервер при каждом запросе, что позволяет хранить больше информации. localStorage особенно полезны для сохранения индивидуальных настроек — фильтров, размеров окон на сайте, выбора светлой/темной темы и т.д. Благодаря свойствам и размеру хранилища, с некоторыми приложениями юзер может работать и в офлайне.</p><h2>SessionStorage</h2><p>Способ работы с данными sessionStorage во многом аналогичен localStorage, но есть принципиальное отличие — после закрытия браузера данные полностью удаляются. Это не означает, что sessionStorage — хуже, просто у него другая сфера применения. В частности формат идеален для хранения данных, актуальных в рамках конкретной сессии — например, информации при заполнении формы или вводе данных банковской карты.</p><figure><img src="https://media.tproger.ru/user-uploads/101078/2025-02-25/c4040c74-e2e9-41b5-a18b-7cf41465d5b3.png" alt="" /></figure><p>Объем хранилища ограничен, но в большинстве случаев этого достаточно для хранения инфы в пределах одного сеанса. Данные по сути не защищены, так что вопрос приватности — забота пользователя.</p><p>Хранилище сеансов поддерживается всеми актуальными браузерами — данные находятся в полной доступности, пока открыты вкладки и окна. Если пользователь закрывает окно, происходит автоматическая очистка.</p><p>Для подключения сеансового хранилища разработчики на JavaScript используют объект sessionStorage и методы для получения, установки и удаления данных.</p><p>Пример кода:</p><p>Как видите, есть определенное сходство с localStorage, поскольку у хранилищ одинаковый алгоритм работы. Метод setItem() применяется для сохранения данных (первый аргумент — ключ, второй — значение), метод получения — removeItem(), для очистки используется метод clear().</p><p>Основные характеристики sessionStorage:</p><ul><li>данные хранятся в течение одной сессии, после закрытия браузера очищаются;</li><li>хранилище использует контекст браузера верхнего уровня, то есть в каждой вкладке содержатся уникальные данные;</li><li>объем данных — 5 МБ;</li><li>старые браузеры формат не поддерживают.</li></ul><p>Если вы открываете другую вкладку с той же страницей, там будут храниться уже другие данные. При этом перезагрузка страницы на данные не влияет, но если прервется сессия (например, временно отвалится интернет) информация обнулится.</p><p>Способ SessionStorage применяют для сохранения данных неавторизованных посетителей в онлайн-магазинах — какие товары он смотрел, какие положил в корзину. Формат используется также в случаях, когда применение localStorage нецелесообразно — например, при заказе еды в ресторанах. В локальном хранилище посетитель бы увидел прошлый заказ, который он собирал и не оформил, но меню к тому времени может поменяться. В сессионном хранилище актуальный заказ будет собран по новой.</p><h2>Сравнение cookies, localStorage и sessionStorage</h2><p>Принципиальная разница между способами хранения понятна из предыдущих разделов, осталось рассмотреть конкретные критерии.</p><h2>Размер хранилища</h2><ul><li>Файлы cookie. Здесь размер минимальный. Для каждого домена куки могут хранить ограниченный объем данных — как правило, не больше 4 КБ.</li><li>LocalStorage. Максимально возможный объем данных для каждого домена — не менее 5 МБ, а при использовании определенных настроек и больше.</li><li>SessionStorage. Здесь хранится больше данных для одного домена в сравнении с cookies. Размер — не более 5 МБ.</li></ul><h2>Срок хранения</h2><ul><li>Файлы cookie. Данные куки могут быть сеансовыми или постоянными в зависимости от настроек. Постоянные имеют свой срок действия, который исчисляется месяцами или даже годами, сеансовые уничтожаются сразу после завершения сеанса.</li><li>LocalStorage. Данные в локальном хранилище остаются после закрытия браузера и его повторного открытия. Удалить их может только пользователь или код JavaScript.</li><li>SessionStorage. Информация в сеансовом хранилище остается, пока открыта вкладка сайта. При закрытии данные удаляются.</li></ul><h2>Работа с данными</h2><ul><li>Файлы cookie. Разработчик может установить срок действия для файлов, после которого браузер автоматически очистит куки.</li><li>LocalStorage. Нельзя запрограммировать автоматическое удаление данных или другие действия с ними. Они сохраняются, пока не будут удалены принудительно.</li><li>SessionStorage. Здесь нет механизма автоматического истечения срока действия. Поэтому данные будут актуальны, пока открыта страница.</li></ul><h2>Доступ к данным</h2><ul><li>Cookies. Доступ к файлам можно получить и изменить через JavaScript API, но существуют определенные ограничения для третьих лиц.</li><li>LocalStorage и sessionStorage. Есть прямой доступ к информации в хранилищах через API JavaScript, который позволяет манипулировать данными.</li></ul><h2>Сетевой трафик</h2><ul><li>Cookies. Данные автоматически отправляются на север после каждого HTTP-запроса для соответствующего домена, что увеличивает загрузку трафика.</li><li>LocalStorage и sessionStorage. Данные не отправляются на сервер, а остаются на стороне клиента, что сокращает объем лишнего трафика.</li></ul><h2>Когда использовать cookies, localStorage и sessionStorage</h2><p>Использование различных способов хранения информации зависит от целей обращения с данными. На сайтах с авторизацией и значимой историей просмотра (например, в крупных онлайн-магазинах) важно, чтобы пользовательские данные сохранялись в большом объеме и как можно дольше, поэтому предпочтительным инструментом выступает localStorage.</p><p>В других случаях достаточно sessionStorage — например, для неавторизованных пользователей хранить информацию дольше одного сеанса не требуется. Cookies применяются, когда нужно отправить небольшое количество данных на сервер — этот способ используют практически все современные веб-ресурсы при работе с информацией, требующей быстрого обновления.</p><p>Разницу в жизненном цикле cookies, localStorage и sessionStorage используют в соответствии с задачами хранения. Ограниченный срок действия cookies и данных сессионного хранилища могут стать преимуществами в одном случае и недостатками в другом. С данными localStorage аналогичная ситуация — если нужен полный контроль времени хранения, этот формат идеально подходит.</p><h2>Проблемы и перспективы хранилищ данных</h2><p>Все хранилища потенциально уязвимы для XSS-атак. В cookies применяют дополнительные меры защиты: флаги HTTPOnly и Secure запрещают доступ из JavaScript к файлам, чувствительным к безопасности. Для отправки таких файлов используется передача по шифрованным каналам HTTPS.</p><p>Для защиты от атак CSRF (подделка межсайтовых запросов) используют атрибут SameSite, проверку HTTP_Referer/Origin или применение синхронизированных токенов.</p><p>В localStorage для обеспечения безопасности важно соблюдать принцип одного источника и тоже пользоваться зашифрованными HTTPS-соединениями. При этом эксперты все равно не рекомендуют сохранять конфиденциальные данные в localStorage.</p><p>Учитывайте, что куки могут замедлять загрузку, особенно если у пользователей низкая скорость интернета. LocalStorage на HTTP-запросы не влияет, что положительно отражается на производительности веб-ресурса.</p><p>После появления пятой версии HTML localStorage становится более востребованным элементом при разработке веб-приложений. Он обеспечивает полноценной контроль данных с сохранением актуальности проекта, идеально подходит для хранения структурированной информации и ее дальнейшей обработки. При этом инструмент поддерживает синхронизацию данных, когда пользователь открывает приложение в разных вкладках.</p>]]></content:encoded>
    </item>
    <item>
      <title>Рефакторинг запросов: как ускорить работу API без переписывания всего кода</title>
      <link>https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda</link>
      <comments>https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda</guid>
      <description><![CDATA[<p>Рефакторинг запросов. Показываем, как ускорить работу API без переписывания всего кода. Рассматриваем пошаговую инструкцию и основные нюансы ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda">Рефакторинг запросов: как ускорить работу API без переписывания всего кода</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 24 Feb 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>API тормозит, а переписывать код с нуля — не вариант? Рефакторинг запросов поможет ускорить работу без радикальных изменений. Разбираем, как оптимизировать API, сокращая задержки и снижая нагрузку на сервер, не разваливая всю систему.</p><h2>Анализируем производительность API</h2><p>Прежде чем ускорять API, нужно понять, что именно замедляет его работу. Для этого оцениваем ключевые метрики:</p><ul><li>Время отклика — сколько времени проходит от запроса до получения ответа.</li><li>Нагрузка на сервер — сколько ресурсов потребляет API при обработке запросов.</li><li>Частота ошибок — как часто сервер возвращает некорректные ответы.</li></ul><p>Отслеживать эти показатели помогают инструменты вроде Postman, New Relic и APM-систем. Они визуализируют данные, автоматизируют тестирование и позволяют находить проблемы в режиме реального времени.</p><h3>Postman</h3><p>Используется для ручного тестирования запросов. Postman показывает время отклика и ошибки. Также через него удобно тестировать сценарии работы API.</p><p>Инструмент удобен тем, что поддерживает коллекции запросов. Это упрощает тестирование сложных цепочек операций во время рефакторинга. Можно интегрировать Newman для автоматизации тестов и анализа метрик на регулярной основе.</p><p><a href="https://tproger.ru/articles/gajd-po-rabote-s-postman">Большой гайд по работе с Postman API Platform</a></p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/bec33c5a-fa3c-4888-af85-885ece3d6f85.jpg" alt="" /><figcaption>Интерфейс Postman</figcaption></figure><h3>New Relic</h3><p>Платформа для мониторинга производительности приложений. Можно отслеживать время обработки API-запросов, статистику по операциям, загрузку системы.</p><p>New Relic показывает распределение времени выполнения между сервером, БД и внешними сервисами, что помогает в рефакторинге разных частей системы.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/732eea24-d64a-4e3b-9585-f2e4f23e2f75.png" alt="" /><figcaption>Интерфейс New Relic</figcaption></figure><h3>APM-системы</h3><p>Инструменты Datadog, AppDynamics или Dynatrace сохраняют данные о том, как работает API. Через них можно смотреть трассировку запросов, выявлять проблемы с медленными вызовами или зависимостями от внешних систем.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/55c2a216-1a7c-46c9-b19a-3324bc9a9493.jpg" alt="" /><figcaption>Трасса в Datadog</figcaption></figure><h3>Ищем узкие места в запросах</h3><p>Для выявления проблемных фрагментов кода необходимо посмотреть, как API реагирует на разные нагрузки, и как распределяется время обработки запроса.</p><p>Поиск состоит из 7 этапов:</p><ul><li><b>Анализ общей картины</b>. С помощью APM-систем и логирования нужно выделить самые медленные запросы.</li><li><b>Локализация длинных операций</b>. В одном запросе может быть несколько узких мест (тяжелый SQL-запрос, внешний API-вызов).</li><li><b>Поиск запросов с высокой частотой вызовов</b>. Например, запросы к слою авторизации или ручки API, к которым обращаются массово при каждом действии пользователя.</li><li><b>Проверка ошибок</b>. Ошибки приводят к дополнительной нагрузке: например, если клиент совершает повторные запросы после тайм-аута.</li><li><b>Анализ распределения нагрузки</b>. Если одни эндпоинты перегружены трафиком, а другие используются редко, в рамках рефакторинга нужно сбалансировать нагрузку. Например, добавить серверы только под обработку горячих эндпоинтов.</li><li><b>Тестирование в условиях пиковой нагрузки</b>. Стресс-тест средствами JMeter поможет выявить проблемы, скрытые в условиях обычного трафика.</li><li><b>Анализ зависимостей API</b>. Замедленная работа внешнего сервиса увеличивает время отклика для каждого запроса. Через Jaeger или Zipkin можно посмотреть цепочку зависимостей.</li><li><b>Локализация длинных операций</b>. В одном запросе может быть несколько узких мест (тяжелый SQL-запрос, внешний API-вызов).</li></ul><h2>Оптимизация запросов к базе данных</h2><p>Уменьшить время отклика API без переписывания кода можно за счёт оптимизации работы с БД. Один из ключевых инструментов — <a href="https://tproger.ru/articles/indeksy-v-postgresql">индексы</a>. Они ускоряют поиск данных, снижая нагрузку на сервер.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/2e25a089-667a-4002-9967-66601f996cfa.jpg" alt="" /><figcaption>B-Tree индекс PostgreSQL</figcaption></figure><p>Что индексировать в первую очередь:</p><ul><li>Поля, используемые в WHERE, JOIN, ORDER BY, GROUP BY. Например, если запросы часто фильтруют по email, имеет смысл добавить индекс.</li></ul><ul><li>Запросы с несколькими условиями фильтрации — их лучше оптимизировать составными индексами. Важно, чтобы порядок полей соответствовал порядку в SQL-запросах.</li></ul><ul><li>Только те данные, где индексация действительно ускорит поиск. Например, индекс для gender (male/female) не даст прироста скорости.</li></ul><p>Лишние индексы замедляют операции записи, их нужно удалять. В PostgreSQL они могут пересекаться, но при дублировании  замедляют запросы. Вместо нескольких отдельных индексов эффективнее использовать составные.</p><h3>Агрегация и выбор только необходимых полей</h3><p>Обработка бесполезных данных увеличивает объем передаваемой информации, замедляет чтение и создает нагрузку на сеть.</p><p>Например, команда SELECT * выбирает все колонки таблицы, включая неиспользуемые. Вместо SELECT * FROM users следует указывать целевые поля: SELECT id, name, email FROM users.</p><p>Если к запросу добавлены ненужные JOIN-ы, группировки или сортировки, в рамках рефакторинга нужно постараться избавиться от них. Избыточные операции на стороне БД рекомендуется переносить в бизнес-логику приложения.</p><p>Снизить объем данных можно за счет COUNT, SUM, AVG, MAX, MIN — эти функции возвращают обобщенные записи вместо отдельных значений. Частичную обработку данных можно выполнять на стороне БД. Например, в PostgreSQL есть встроенные функции по типу JSON_AGG.</p><h3>Пагинация и лимиты в запросах</h3><p>API, работающие со списками пользователей и транзакциями, обязательно должны использовать пагинацию. Лимит на объем возвращаемых записей предотвращает перегрузку серверов и клиента.</p><p>Самое простое решение — во время рефакторинга ограничить количество строк с помощью LIMIT или FETCH FIRST. Например, вместо загрузки всех пользователей SELECT id, name FROM users LIMIT 30 вернет только первые 30 строк.</p><p>Еще можно использовать постраничную выборку (комбинацию LIMIT и OFFSET):</p><p>Если производительность упала, значит офсет слишком большой. В таких случаях лучше перейти на модель курсоров.</p><p>Пагинация с курсорами заменяет OFFSET значением последнего взятого элемента. Например, вместо LIMIT 20 OFFSET 100 можно использовать запрос:</p><p>Чтобы пагинация работала корректно, всегда нужно использовать явный ORDER BY.</p><p><a href="https://tproger.ru/articles/realizuem-paginaciju-v-go-ispolzuja-postgresql">Пагинация на Go в PostgreSQL</a></p><h2>Кэширование</h2><p>Количество запросов к серверу можно сократить, если сохранять часто запрашиваемые данные. Это называется <b>кэшированием</b>. Рефакторинг кэширования уменьшает нагрузку на сервер, снижает время отклика API.</p><p>Рассмотрим инструменты для кэширования.</p><h3>Redis</h3><p>Подходит для серверного и распределенного кэширования. Поддерживает TTL, работу со списками, хэшами и включение репликации для отказоустойчивости. Для интеграции доступны библиотеки и клиенты — ioredis для Node.js или StackExchange.Redis для .NET.</p><p><a href="https://tproger.ru/articles/kak-ispolzovat-redis-dlya-kewirovaniya-i-ocheredej-v-veb-prilozheniyah">Как использовать Redis для кэширования и очередей в веб-приложениях</a></p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/a5b5085d-4c58-45f0-b8f7-b27fcd78ad85.jpg" alt="" /><figcaption>Схема работы Redis, данные хранятся в оперативной памяти сервера</figcaption></figure><p>Рефакторинг кэширования повысит производительность сервиса:</p><ul><li>если API возвращает неизменяемые или редко изменяющиеся данные,</li><li>если часть запросов к БД выполняется кратно дольше остальных.</li></ul><p>Например, в Redis можно сохранять список активных пользователей:</p><h3>Memcached</h3><p>Применяется для уменьшения нагрузки на базу данных и работы с временными данными. Memcached менее функционален, чем Redis, но более эффективен для сценариев, где не требуется сложная логика или постоянное хранилище.</p><p><a href="https://tproger.ru/articles/kak-ispolzovat-servery-redis-i-memcached-dlya-kewirovaniya">Как использовать серверы Redis и Memcached для кэширования</a></p><h3>Виды кэша</h3><p>Выбор типа кэширования зависит от архитектуры API и характера данных.</p><ul><li>Клиентский кэш — данные хранятся на стороне клиента. Например, браузеры загружают страницу дольше в первый раз, но затем мгновенно извлекают её из кэша.</li><li>Серверный кэш — данные сохраняются на сервере или в промежуточном хранилище (Redis, Memcached). Это снижает нагрузку на базу данных: повторные запросы возвращаются из кэша, а не пересчитываются заново.</li><li>Распределённый кэш — данные кэшируются в нескольких узлах для масштабирования и высокой доступности. Например, Redis в режиме кластера помогает организовать кэширование в микросервисной архитектуре.</li></ul><h2>Уменьшение объема передаваемых данных</h2><p>Скорость API зависит от объема данных, передаваемых между клиентом и сервером.</p><h3>Оптимизация формата ответа: JSON vs Protobuf</h3><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/d023e3be-de47-4800-9eed-c034b97920c4.jpg" alt="" /><figcaption>Сравнение JSON и Protobuf</figcaption></figure><p>Формат <b>JSON </b>популярен из-за читаемости и универсальной поддержки большинством языков программирования. Он менее эффективен по сравнению с бинарными форматами. JSON занимает больше места из-за текстового представления структур и значений.</p><p><b>Protobuf </b>(Protocol Buffers) —  бинарный формат, разработанный Google. Он компактен, быстрее сериализуется и занимает меньше места по сравнению с JSON. В формате Protobuf каждый элемент закодирован с минимальным количеством байтов.</p><p>Переход на Protobuf может потребовать изменений в клиентах API, поэтому такая оптимизация выполняется на этапе, когда остальные методы снижения объема данных себя исчерпали.</p><h3>Удаление избыточных данных и компрессия</h3><p>Часто API отправляет больше данных, чем реально нужно клиенту. Это лишний сетевой трафик и задержки:</p><ul><li>Удаление ненужных полей — ограничение выборки данных на стороне сервера: используются выборочные запросы к БД, настройка сериализаторов или фильтрация на уровне представлений.</li><li>Сжатие Gzip — уменьшение размера текстовых данных (JSON, XML) на 70–90%, не требуя изменений в API. Включается на уровне Nginx, Apache или через middleware. Клиенты автоматически распаковывают такие ответы, сохраняя прозрачность процесса.</li></ul><h3>Версионирование API</h3><p>Клиенты обычно нуждаются в данных разного объема. Вместо универсального ответа для всех пользователей рекомендуется разработать несколько версий API.</p><p>Новые версии должны отправлять клиентам упрощенные данные или предлагать другую модель представления без модификации существующих запросов.</p><p>Упрощение достигается через фильтрацию полей на стороне сервера с использованием сериализаторов или форматов ответа. Например, через GraphQL можно сделать так, чтобы клиенты напрямую запрашивали только необходимые поля.</p><p>Чтобы не произошло одновременного отказа у клиентов на старых версиях API, можно временно поддерживать несколько версий. Со временем старую версию нужно объявить устаревшей и отключить.</p><h2>Параллелизация и объединение запросов</h2><p>Оптимизировать API можно за счет рефакторинга batch-запросов. Они объединяют несколько запросов в один — количество обращений между клиентом и сервером сокращается.</p><p>Клиенты отправляют массив запросов, сервер обрабатывает их и возвращает всего один ответ.</p><p>Batch-запросы дают прирост к производительности, когда клиент ожидает получение или обновление нескольких независимых ресурсов.</p><p>Например, мобильное приложение может запрашивать информацию о пользователе и связанных с ним объектах (сообщения, уведомления) за один вызов.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/c893e38f-6031-4a2a-a861-b911798ce759.jpg" alt="" /><figcaption>Схематичное изображение batch-запросов</figcaption></figure><p>Если API поддерживает вложенные запросы, клиент может запрашивать связанные данные одним вызовом. Пример на GraphQL:</p><h3>Асинхронные операции</h3><p>API после рефакторинга будет работать быстрее, если выполнять запросы независимо друг от друга. Сервер может распределять выполнение независимых операций по асинхронным потокам.</p><p>Например, при обработке одного запроса API может обратиться к нескольким подсистемам, отправить синхронные запросы в базу данных и внешние API, параллельно собирая результаты.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/d1a8d43e-6ba2-4c84-866f-caae7301910a.jpg" alt="" /><figcaption>Принцип работы асинхронного API</figcaption></figure><p>Для асинхронных операций часто используются очереди сообщений RabbitMQ или Apache Kafka.</p><h2>Оптимизация на уровне серверной логики</h2><p>Обработку запросов можно откладывать до момента, когда данные действительно понадобятся. Это полезно при работе с запросами, где связанная информация не требуется или используется не полностью. Такое поведение называется <b>lazy loading</b> (ленивая загрузка).</p><p>Загружать связанные данные можно заранее в одном запросе, чтобы сократить количество запросов к БД. Такой принцип работы API называется <b>eager loading</b> (стремительная загрузка). Используется в случаях, когда известно, что все связанные данные понадобятся в ходе операции.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/56dd35a2-1b7c-437e-b796-cc3ef56b2ea0.jpg" alt="" /></figure><p><b>Lazy loading</b> подходит, если только небольшая связка данных используется в каждом запросе.</p><p><b>Eager loading</b> лучше применять, когда количество запросов к БД критично или весь объем данных необходим для выполнения операции.</p><h3>Очереди для обработки задач в фоне</h3><p>Вместо выполнения тяжелых задач в реальном времени, сервер может добавлять их в очередь, чтобы они обрабатывались фоновыми воркерами.</p><p>Очереди используются для задач, не влияющих на основной ответ клиенту:</p><ul><li>отправка писем,</li><li>формирование отчетов,</li><li>обновление статистики,</li><li>построение индексов,</li><li>обработка данных.</li></ul><p>При поступлении запроса API выполняет только основные операции (валидацию данных или запись в базу), после чего создает задачу в очереди. Процессы выполняются в отдельной среде, не влияя на основной поток обработки запросов.</p><h3>Разделение сложных операций на этапы</h3><p>Комплексные операции, например, генерации отчетов, можно разделить на сбор данных из базы, их обработку и финальное преобразование. Каждый шаг выполняется независимо, а результаты промежуточных операций сохраняются для последующего использования.</p><p>Вместо последовательного выполнения всех шагов, задачу можно поручить системе планировщиков или pipeline в <a href="https://tproger.ru/articles/kak-nastroit-i-ispolzovat-jenkins-dlya-avtomatizacii-processov">Jenkins</a>. Каждый этап становится в очередь и выполняется отдельным процессом.</p><h2>Мониторинг и тестирование</h2><p>После рефакторинга хочется быть уверенным в стабильности API, выявить возможные проблемы и оценить эффективность оптимизаций. Процесс включает:</p><ul><li>нагрузочное тестирование,</li><li>сравнение метрик до и после рефакторинга,</li><li>автоматизацию наблюдения за состоянием API.</li></ul><p>Нагрузочное тестирование определяет, как API справляется с возрастающей нагрузкой. Оно выявляет точки перегрузки. Инструменты для стресс-теста API: Apache JMeter, Gatling.</p><p>На основе результатов нагрузочного тестирования можно проанализировать, насколько рефакторинг улучшил производительность API. В сборе метрик помогут APM-инструменты.</p><p><b>Если показатели значительно улучшились, поздравляем, рефакторинг удался. </b></p><p>Автоматизация мониторинга повышает надежность API и предотвращает проблемы до их масштабного проявления. Инструменты для поддержания производительности в реальном времени: Prometheus + Grafana, Datadog.</p><h2>Что запомнить</h2><ul><li>Производительность API оценивается с помощью метрик: времени отклика, нагрузки на сервер и частоты ошибок.</li><li>Для мониторинга API используются инструменты Postman, New Relic и APM-системы, которые выявляют узкие места системы.</li><li>Оптимизация запросов к базе данных включает индексацию, удаление избыточных операций и выбор только необходимых полей. Для работы с большими объемами данных используется пагинация с применением LIMIT, OFFSET или курсоров.</li><li>Кэширование снижает нагрузку на сервер, сохраняя востребованные данные локально.</li><li>Уменьшение объема передаваемых данных достигается за счет перехода с JSON на Protobuf, компрессии Gzip и фильтрации полей.</li><li>Для сокращения числа запросов к API применяются batch-запросы.</li><li>Асинхронная обработка операций с помощью очередей RabbitMQ или Kafka ускоряет время отклика API для клиентов.</li><li>Очереди для фоновой обработки задач снижают нагрузку на основной поток запросов.</li><li>Нагрузочное тестирование помогает оценить улучшения после рефакторинга.</li><li>Автоматизация мониторинга предотвращает проблемы API до их критического проявления.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Как грамотно тестировать API: от спецификации до тест-кейсов</title>
      <link>https://tproger.ru/articles/api--nachalo-testirovaniya</link>
      <comments>https://tproger.ru/articles/api--nachalo-testirovaniya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Елизавета Ржевская]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/api--nachalo-testirovaniya</guid>
      <description><![CDATA[<p>Узнайте, как правильно тестировать API: разберёмся со спецификацией, best practices, выявлением ошибок и составлением чек-листов и тест-кейсов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/api--nachalo-testirovaniya">Как грамотно тестировать API: от спецификации до тест-кейсов</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 22 Feb 2025 09:01:34 GMT</pubDate>
      <content:encoded><![CDATA[<p>API — не просто набор методов, а основа надёжного и масштабируемого сервиса. Ошибки в нём дорого обходятся, а значит, тестирование — не опция, а необходимость. Как проверить API на прочность и избежать критических сбоев? Вместе с тестировщицей Новео Татьяной разберём ключевые этапы: от анализа спецификаций до создания тест-кейсов.</p><h2>Есть спецификация API? Отлично!</h2><p>Первый и самый важный шаг перед началом тестирования — получить спецификацию API. Чаще всего она представлена в виде документации, которая может находиться в Google Документах, Swagger, OpenAPI или другом формате. Спецификация API —  описание всех возможностей, предоставляемых интерфейсом, включая методы, параметры, структуры запросов и ответов, а также возможные ошибки. Она помогает понять, как должен работать интерфейс и чего от него ожидать на уровне данных и взаимодействия.</p><p>Как эффективно работать со спецификацией:</p><ul><li>Разберите каждый эндпоинт: какие параметры обязательны, какие нет.</li><li>Определите HTTP-методы (GET, POST, PUT, DELETE) и их назначение.</li><li>Изучите форматы запросов и ответов (обычно JSON или XML).</li></ul><p>Спецификация — ваш главный инструмент при тестировании API. Но если документации нет или она неполная, уточните детали у команды, чтобы не тестировать API вслепую.</p><h2>Ищем спецификацию API и гуглим best practices</h2><p>Даже с подробной спецификацией полезно сверяться с отраслевыми стандартами. Например, для REST API существуют рекомендации по структуре запросов, кодам ответов и организации данных.</p><p>REST — не просто способ передачи данных, а архитектурный стиль, описанный Роем Филдингом. Его принципы помогают сделать API удобным, поддерживаемым и масштабируемым.</p><p>Ключевые best practices для REST API:</p><ul><li>Правильные HTTP-методы: GET — для получения, POST — для создания, PUT — для обновления, DELETE — для удаления.</li></ul><ul><li>Четкие коды ответа: 200 — успех, 404 — ресурс не найден, 500 — ошибка сервера.</li></ul><ul><li>Понятные URL: /users/123 вместо getUserById?id=123.</li></ul><ul><li>Фильтрация и пагинация: если данных много, дайте пользователю инструменты для работы с ними.</li></ul><p>Если API не следует этим принципам, это не просто вопрос удобства — возможны проблемы с поддержкой и интеграцией. В таком случае стоит обсудить с командой возможные доработки.</p><h2>Если находим отличия от best practices — пишем разработчикам</h2><p>Не все отклонения от best practices — ошибки. Иногда у команды есть технические или бизнес-обоснования для нестандартных решений. Главное — зафиксировать такие моменты и обсудить их с разработчиками.</p><p>Пример:</p><p>GET обычно используется для получения данных, но он ограничен длиной URL. Если запрос содержит сложные фильтры, иногда используют POST, хотя это и не соответствует стандарту. Важно выяснить, почему принято такое решение, и задокументировать его (кстати, еще примеры <a href="https://habr-com.cdn.ampproject.org/v/s/habr.com/ru/amp/publications/770226/?amp_gsa=1&amp;amp_js_v=a9&amp;usqp=mq331AQIUAKwASCAAgM%3D#amp_tf=From%20%251%24s&amp;aoh=17185355618957&amp;referrer=https%3A%2F%2Fwww.google.com&amp;ampshare=https%3A%2F%2Fhabr.com%2Fru%2Farticles%2F770226%2F">здесь</a>).</p><p>Что делать при обнаружении отклонений:</p><ol><li>Зафиксируйте отличие.</li><li>Запросите у разработчиков или аналитиков объяснение.</li><li>Если отклонение оправдано — внесите его в документацию.</li><li>Если это ошибка — обсудите с командой, как её исправить.</li></ol><p>Фиксация таких нюансов упрощает поддержку API и снижает риск ошибок в будущем.</p><h2>Пишем чек-лист или тест-кейсы</h2><p>После изучения спецификации и обсуждения отклонений от best practices пора переходить к тестовой документации. В зависимости от требований проекта это может быть чек-лист или тест-кейсы.</p><h3>Чек-лист: быстро и просто</h3><p>Чек-лист — это краткий перечень проверок без подробного описания шагов и ожидаемых результатов. Он удобен для быстрого тестирования и контроля ключевых аспектов API.</p><p>Пример чек-листа:</p><p><b>Успешный запрос</b> — отправить корректный запрос и убедиться, что API возвращает 200 OK с ожидаемыми данными.</p><p><b>Аутентификация </b>— запрос без API-ключа должен вернуть 401 Unauthorized.</p><p><b> Некорректные параметры</b> — передать текст вместо числа, убедиться, что API отвечает 400 Bad Request.</p><h3>Тест-кейсы: подробно и детально</h3><p>Тест-кейсы содержат полное описание тестового сценария, включая входные данные, ожидаемые и фактические результаты. Они нужны, если проект требует строгой документации.</p><p><b>Пример тест-кейса:</b></p><p>Проверка метода GET для получения списка пользователей.</p><p>Предусловия:</p><ul><li>Доступ к API.</li><li>В системе есть хотя бы один зарегистрированный пользователь.</li></ul><p>Шаги:</p><ul><li>Отправить GET-запрос на /users.</li></ul><ul><li>Проверить, что API отвечает 200 OK.</li></ul><ul><li>Убедиться, что в ответе содержится список пользователей.</li></ul><p>Ожидаемый результат:</p><p>Код ответа — 200, в теле ответа — массив объектов с полями id, name, email.</p><p><b>Как составлять тесты грамотно?</b></p><p>✔ Опираться на спецификацию. Тесты должны соответствовать заявленному поведению API.</p><p>✔ Думать как пользователь. Предусматривать реальные сценарии использования.</p><p>✔ Покрывать API полностью: позитивные и негативные кейсы, граничные значения.</p><p>Выбор между чек-листом и тест-кейсами зависит от требований проекта, но лучшее тестирование — всегда продуманное и системное тестирование.</p><p>Больше идей для проверок можно найти <a href="https://qarocks.ru/60-test-cases-for-api-testing/#h2">здесь</a>.</p><p>Разговаривайте с командой, фиксируйте отклонения и не забывайте, что здравый смысл и опыт могут сыграть ключевую роль в успешном тестировании API.</p>]]></content:encoded>
    </item>
  </channel>
</rss>