<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Лучшая практика</title>
    <description>Примеры, на которые стоит равняться — уроки мастерства и рекомендации от опытных специалистов.</description>
    <link>https://tproger.ru/tag/best-practice</link>
    <atom:link href="https://tproger.ru/tag/best-practice/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Mon, 28 Sep 2026 17:41:48 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Лучшая практика</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>NULL в SQL: почему запрос отрабатывает и молча отдаёт неверное число</title>
      <link>https://tproger.ru/articles/null-v-sql-pochemu-zapros-otrabatyvaet-i-molcha-otdayot-nevernoe-ch</link>
      <comments>https://tproger.ru/articles/null-v-sql-pochemu-zapros-otrabatyvaet-i-molcha-otdayot-nevernoe-ch?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Tproger]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/null-v-sql-pochemu-zapros-otrabatyvaet-i-molcha-otdayot-nevernoe-ch</guid>
      <description><![CDATA[<p>Почему WHERE x = NULL не находит строки, как одно пустое значение обнуляет весь NOT IN и на что делится среднее. Разбираем на примерах Postgres.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/null-v-sql-pochemu-zapros-otrabatyvaet-i-molcha-otdayot-nevernoe-ch">NULL в SQL: почему запрос отрабатывает и молча отдаёт неверное число</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Анализ данных]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Sep 2026 06:08:25 GMT</pubDate>
      <content:encoded><![CDATA[<p>В SQL деление на ноль Postgres остановит с ошибкой. Приведение строки «abc» к числу тоже. А деление на NULL пройдёт, и запрос вернёт результат. Просто не тот, которого вы ждали.</p><p>Это главная особенность работы с отсутствующими значениями: ошибок нет, есть тихо неправильные числа. Разбираем, откуда они берутся в фильтрах, в арифметике, в агрегатах и в соединениях, и какими операторами это лечится.</p><p>Отправная точка одна: NULL — не значение, а отметка «неизвестно». Неизвестность распространяется дальше по всему выражению: через сравнения, арифметику, склейку строк, агрегаты, оконные функции и условия отбора. Результат при этом строго определён правилами языка, он просто может не совпасть с тем, что вы имели в виду.</p><p>В SQL три логических исхода, а не два: истина, ложь и неизвестность. Условие отбора оставляет только строки с истиной, поэтому неизвестность отбрасывается наравне с ложью.</p><p>Сравнение = NULL не почти верно, а отвечает на другой вопрос. Для проверки на отсутствие есть отдельные операторы, которые сравнением не являются.</p><p>Одно значение NULL в списке для NOT IN обнуляет всю выдачу целиком, а не сужает её.</p><p>Соединение по равенству отбрасывает строки, где ключ отсутствует с обеих сторон, и то же самое делает проверка уникальности.</p><p>Запрос, который отработал, прошёл только проверку грамматики. Число становится верным, когда с вопросом совпали строки, фильтры и знаменатель.</p><h2>Три логических исхода вместо двух</h2><p>Начнём с простого случая. В таблице клиентов у одного из них не заполнен телефон, и это прямо видно глазами. Запрос находит ноль строк:</p><p>Условие phone = NULL спрашивает: «равно ли это неизвестное значение тому неизвестному значению?» Ответить на такое нельзя, поэтому <a href="https://dev.to/systemcraftdev/why-where-x-null-never-works-in-sql-and-what-to-use-instead-3p4h">для каждой строки возвращается неизвестность</a>, включая ту самую строку с пустым телефоном. А отбор оставляет только строки, где условие истинно. Неизвестность не проходит ровно так же, как не прошла бы ложь.</p><p>Правило действует единообразно: NULL = NULL тоже не истина, а неизвестность. Отсюда и главный вывод — оператор равенства нельзя «подправить», чтобы он здесь заработал. Он не почти прав, он отвечает на другой вопрос.</p><p>В Postgres это проверяется одной строкой. Что вернёт такое выражение?</p><p>Оба внутренних сравнения неизвестны, поэтому внешнее сравнивает неизвестность с неизвестностью и тоже даёт неизвестность. Операторы сравнения возвращают NULL, если хотя бы одна сторона неизвестна. Именно поэтому в языке есть отдельный оператор проверки на отсутствие: узнать, равны ли две неизвестности, нельзя, а узнать, является ли значение неизвестным, можно.</p><h3>Как ведут себя И, ИЛИ и НЕ</h3><p>Логические связки тоже работают в трёх значениях, и запомнить стоит два исключения:</p><p>То есть ИЛИ всё ещё истинно, если истинна вторая сторона, а И всё ещё ложно, если ложна вторая сторона. Всё остальное с участием неизвестности схлопывается в неизвестность.</p><p>Последствия неочевидны. Вот запрос, который отбрасывает строку, которую вы почти наверняка хотели оставить:</p><p>В обычной двузначной логике «активно или не активно» — тавтология, утверждение, истинное при любом значении. В SQL это не так.</p><h3>Операторы, которые никогда не возвращают неизвестность</h3><p>Лечится это выходом из трёхзначной логики. Сначала решите, что NULL означает в вашей предметной области, а потом скажите это явно:</p><p>Проверки IS TRUE, IS NOT TRUE, IS FALSE, IS NOT FALSE и IS UNKNOWN никогда не возвращают неизвестность, поэтому строку не потеряют.</p><p>Аналог того же для равенства — IS NOT DISTINCT FROM. Обычное равенство спрашивает, известно ли, что значения совпадают. Этот оператор спрашивает, одно ли это значение, обращаясь с отсутствием как с полноценным состоянием. Две неизвестности неразличимы, поэтому предикат истинен.</p><p><b>Где это пригодится:</b><br />Соединение по условию равенства отбрасывает строки, где ключ отсутствует с обеих сторон. Если два отсутствующих ключа должны считаться совпадением, замените равенство на IS NOT DISTINCT FROM. То же касается проверок уникальности и условий отбора, где «отсутствует» должно означать «отсутствует», а не «неизвестно».</p><h2>Ловушка NOT IN, из-за которой пропадает вся выдача</h2><p>Это самый дорогой из всех эффектов, потому что он не сужает результат, а стирает его целиком. Операторы IN и NOT IN разворачиваются в цепочки сравнений, а NOT IN — в цепочку через И:</p><p>Разберём оба исхода. Если значение совпало с одним из перечисленных, срабатывает то самое правило «И спасает ложь справа», и выражение честно ложно. Если не совпало ни с одним, множитель со сравнением с пустым значением делает всё выражение неизвестным.</p><p>Для условия отбора разницы между этими исходами нет: оно оставляет только истину, а ложь и неизвестность отбрасывает одинаково. Поэтому итог один — если в списке или в подзапросе есть хотя бы одно отсутствующее значение, NOT IN не вернёт ни одной строки. Никакой ошибки при этом не будет.</p><p>Обычный IN ведёт себя мягче, поскольку разворачивается в цепочку через ИЛИ: совпадение остаётся истиной независимо от того, что ещё есть в списке. В простом условии отбора пустое значение в списке вообще ничего не меняет: несовпавшая строка отсеется что при неизвестности, что при лжи. Разница вылезает там, где результат сравнения используется дальше: под отрицанием, в выражении CASE или в ограничении целостности.</p><p>Надёжная замена — NOT EXISTS, который работает через равенство внутри условия отбора и потому никогда не трактует сравнение с отсутствующим значением как совпадение. Тот же смысл выражает антисоединение, которое вдобавок часто даёт лучший план:</p><p>Вычистить отсутствующие значения из подзапроса тоже можно, но это помогает, только если вы уверены, что их следует игнорировать. Вариант с NOT EXISTS делает это намерение явным, а не подразумеваемым.</p><h2>Арифметика: одно значение NULL обнуляет строку</h2><p>Любая арифметическая операция с участием отсутствующего значения даёт отсутствующее значение. Сложение, умножение, деление, модуль, возведение в степень — результат один.</p><p>В отчётах это выглядит как пустые ячейки, а в обновлениях данных как стёртые колонки. Классический пример, который каждый когда-нибудь писал:</p><p>Подставлять значение по умолчанию нужно в той точке, где вам известно правило предметной области, а не механически везде. В разборе Кристофера Уинслетта из Crunchy Data приводится удачная иллюстрация: <a href="https://www.crunchydata.com/blog/postgres-calculations-and-the-ambiguity-of-null">отсутствующее количество почти всегда означает ноль, отсутствующая ставка налога тоже, а вот отсутствующая цена нулём не является</a> и должна оставаться неизвестной, пока её кто-нибудь не заполнит.</p><h2>Агрегаты и оконные функции считают не то, что кажется</h2><p>Здесь отсутствующие значения ведут себя иначе, чем везде: агрегаты их пропускают. Функция COUNT(*) считает строки, а COUNT(колонка) — только непустые значения; SUM, AVG, MIN и MAX пропускают пустые входы.</p><p>У товара с двумя незаполненными оценками среднее и сумма пусты, а число строк равно двум. Отсюда практическое правило, которое экономит часы разбирательств с аналитикой: среднее — это сумма, делённая на количество непустых значений, а не на количество строк. Смешивать SUM(x) / COUNT(*) и ожидать совпадения со средним нельзя.</p><p>Оговорка про типы тоже важна: если сумма и счётчик целочисленные, деление усечёт дробную часть. Шесть, делённое на два, случайно даст ровно три, а оценки 5, 1 и 2 дадут при целочисленном делении двойку вместо 2,67.</p><h3>Где ломаются оконные функции</h3><p>Суммирование и усреднение в окне пропускают отсутствующие значения так же, как при группировке, а ROW_NUMBER считает строку в любом случае. Расхождение начинается у функций, которые смотрят в конкретную позицию окна: LAG, LEAD, FIRST_VALUE, LAST_VALUE и NTH_VALUE.</p><p>Такая функция возвращает то, что лежит в запрошенной позиции. Если там пусто, результат пуст. Ближайшее реальное значение она не ищет, и на рядах измерений с пропущенным отсчётом это даёт разрывы там, где ожидалась непрерывность.</p><h2>Проверка чужого запроса, включая сгенерированный</h2><p>Всё описанное складывается в одну проблему: запрос, который отработал, прошёл только проверку грамматики. База ловит опечатку в имени таблицы. Она не ловит суммирование не той колонки, соединение, задваивающее строки, и фильтр, поставленный не на том этапе. Каждая из этих ошибок — синтаксически корректный SQL.</p><p>Со сгенерированными запросами есть <a href="https://dev.to/michaelnocito/how-to-review-ai-generated-sql-before-you-trust-the-number-19ek">отдельная сложность</a>: они беглые. Псевдонимы аккуратные, форматирование чистое, форма выглядит как работа внимательного человека. Беглость читается как правильность, хотя это разные вещи.</p><h3>Проверка первая: посчитайте строки до того, как поверите сумме</h3><p>Задача звучала как «чистая выручка по завершённым заказам». Полученный запрос:</p><p>Он отрабатывает и возвращает 1830. Правильный ответ 1330, и это легко проверить руками: валовая сумма одиннадцати завершённых заказов равна 1605, возвраты составляют 275.</p><p>Виновато соединение. Два заказа возвращались частями, по две записи на каждый, поэтому одиннадцать строк превращаются в тринадцать, а сумма по заказам считает эти два заказа дважды: 2105 вместо 1605. Лишние 500 — это ровно стоимость задвоенных заказов. Явление называется размножением строк: соединение множит строки всякий раз, когда ключ на другой стороне встречается больше одного раза.</p><p>Проверка стоит двух запросов:</p><p>Одно это сравнение всё решает. Второе число выросло, значит, соединение размножило строки, и любая сумма или среднее по колонкам левой таблицы под подозрением. Число совпало — соединение безопасно, идём дальше.</p><h3>Проверка вторая: ищите отсутствующие значения в каждом фильтре</h3><p>Следующая просьба звучала как «та же выручка, но без служебных аккаунтов», и запрос получился такой:</p><p>Он возвращает пустоту, полученную из нуля строк. Не меньшее число, а вообще ничего. Механизм вы уже знаете: в справочнике служебных аккаунтов есть одна строка с незаполненным идентификатором, а справочники в реальной жизни такими и бывают. Одна такая строка бесшумно опустошает результат целиком.</p><h2>Что забрать с собой</h2><p>Правила про отсутствующие значения не сложны, они просто действуют не там, где их ждут. Из фильтра строка пропадает молча, арифметика превращает результат в пустоту, соединение по равенству теряет пары без ключа, а среднее делит на другой знаменатель. Ошибки при этом нигде не возникает, и потому такие дефекты живут в отчётах годами.</p><p>Самый надёжный способ не разбираться со всем этим каждый раз — не хранить отсутствующие значения там, где они не нужны. Если колонка обязана иметь значение, скажите это в схеме. Ограничение NOT NULL выглядит формальностью ровно до первого расследования, почему выручка в отчёте оказалась на пятьсот единиц больше настоящей.</p><p>Материалы разбора: <a href="https://dev.to/systemcraftdev/why-where-x-null-never-works-in-sql-and-what-to-use-instead-3p4h">почему сравнение с NULL никогда не срабатывает</a>, <a href="https://www.crunchydata.com/blog/postgres-calculations-and-the-ambiguity-of-null">поведение NULL в вычислениях Postgres</a> и <a href="https://dev.to/michaelnocito/how-to-review-ai-generated-sql-before-you-trust-the-number-19ek">проверка сгенерированного SQL до того, как поверить числу</a>.</p><p>Откройте последний отчёт, который вы отдавали наружу, и сравните в нём количество строк до и после соединений. Проверка занимает пять минут и иногда меняет цифру, на которую уже сослались в переписке.</p>]]></content:encoded>
    </item>
    <item>
      <title>Ваши open source-контрибьюторы теперь ИИ-first. Как не утонуть в потоке агентских пулреквестов</title>
      <link>https://tproger.ru/articles/vawi-kontribyutory-teper-ai-first-kak-ne-utonut-v-potoke-agen</link>
      <comments>https://tproger.ru/articles/vawi-kontribyutory-teper-ai-first-kak-ne-utonut-v-potoke-agen?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/vawi-kontribyutory-teper-ai-first-kak-ne-utonut-v-potoke-agen</guid>
      <description><![CDATA[<p>Агенты генерируют пулреквесты в open source. Разбираем опыт AutoGPT: как ставить ворота, писать AGENTS.md и не тратить команду на ревью слабых PR.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/vawi-kontribyutory-teper-ai-first-kak-ne-utonut-v-potoke-agen">Ваши open source-контрибьюторы теперь ИИ-first. Как не утонуть в потоке агентских пулреквестов</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Методологии разработки]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 19 Aug 2026 07:01:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если в очереди на ревью вашего open source-проекта всё чаще оказываются пулреквесты, написанные не человеком, а агентом, — вы не одни. Чтобы не утонуть в этом потоке, мейнтенеру нужно перенести правила рядом с кодом и настроить автоматические ворота: шаблон PR, CI, требования к тестам и лицензионное соглашение участника.</p><p>В AutoGPT, где на момент интервью у репозитория было свыше 180 тысяч звёзд и 150 открытых PR, большая часть этих PR создана агентами: Copilot, OpenClaw, собственными инструментами команды и сторонними ботами. Большинство мейнтенеров реагируют просто: закрыть дверь, отключить пулреквесты, не тратить силы на чужой LLM-вывод. Но у Николаса Тиндла, одного из основателей ИИ-инженерии в AutoGPT, другой взгляд: «По сути, кто-то другой платит за ваши вычислительные ресурсы. Если контрибьютор хочет потратить свои токены на улучшение вашего проекта — пусть тратит. Главное, сделать так, чтобы единственный путь внутрь проходил через ваши правила.»</p><p>ИИ-first контрибьютор — это разработчик, который отдаёт написание кода, тестов или документации генеративной модели, либо самоходный агент, который клонирует репозиторий, ищет issue, пишет код и открывает PR. Для мейнтенера результат одинаков: в репозиторий приходит поток кода, который часто не учитывает контекст проекта и требует ревью.</p><p>Агенты не ищут документацию сами — они читают то, что лежит рядом с кодом. AGENTS.md и CLAUDE.md в нужных директориях работают лучше, чем вики.</p><p>Пропускные ворота должны быть автоматическими и однозначными: шаблон PR, тест-план, CI как стена, CLA как детектор человека.</p><p>Не все ворота стоит оставлять включёнными: автоматические комментарии об ошибках CI быстро превращаются в шум.</p><p>Плохой AGENTS.md хуже, чем его отсутствие: избыточные инструкции засоряют контекст и ломают поведение агентов.</p><p>Мейнтенер всё ещё решает, что принимать: закрыть PR и переписать самому — тоже валидный выбор.</p><h2>Проблема не в документации, а в её обнаружении</h2><p>Первое, что пробовала сделать AutoGPT, — улучшить CONTRIBUTING.md, написать подробную вики и обновить гайды. Это не сработало. Дело в том, что агенты не ходят по ссылкам и не ищут документацию в разделах репозитория. Они смотрят на то, что находится в текущей директории и на один-два уровня выше. Если инструкция не лежит там, где агент работает, для него её не существует.</p><p>Сначала команда добавила файлы CLAUDE.md, потому что Claude открывал пулреквесты без достаточного контекста о репозитории. Потом выяснилось, что Copilot и Codex эти файлы игнорируют — они не Claude. Решением стал централизованный AGENTS.md, на который указывают все специфичные для модели файлы.</p><p>Важный нюанс: AGENTS.md действует внутри директории. Если агент работает в backend/, он видит правила для бэкенда. Если динамически загружается навык, связанный с фронтендом, агент может не знать, в какой директории искать инструкции — и навык сам должен ему подсказать путь. Поэтому в AutoGPT файл AGENTS.md лежит рядом с кодом, который он регулирует.</p><p><b>Навык — что это?</b><br />В терминологии агентских инструкций навык — это файл с описанием, по которому агент решает, когда загружать полный набор правил. Агент сканирует описания заранее и подключает нужный навык, когда задача совпадает. Например: «напиши Storybook-тест, если компонент лежит в этих папках».</p><p>Фронтенд-инженер AutoGPT устал от одного и того же класса сломанных PR в open source-проекте и оформил гайд как навык. Теперь каждый агент, который касается репозитория, видит триггер и выполняет правило. Бэкенд делает то же самое: не набрал 80% покрытия — PR не пройдёт.</p><h2>Ворота для ИИ-контрибьюторов в open source</h2><p>AutoGPT выстроила несколько механизмов, которые сдерживают поток низкокачественных пулреквестов и заставляют агентов вести себя предсказуемо.</p><h3>Шаблон пулреквеста как пропускной пункт</h3><p>В шаблоне PR прямо написано: PR, не соответствующий шаблону, закрывается автоматически и без колебаний. Команда даже построила бота, который это делает. Но запускать его не понадобилось — само правило изменило поведение агентов. Агенты стали заполнять шаблон. А вот люди иногда его игнорировали, и это Тиндл считает полезным сигналом: «Если вы не следуете шаблону, вы, скорее всего, человек, и я отнесусь к вам мягче.»</p><h3>Тест-план, который запускает код</h3><p>В шаблоне есть раздел тест-плана, и его формулировка незаметно подсказывает агенту протестировать пулреквест. Эта фраза активирует навык test PR: агент устанавливает браузер, разворачивает приложение и проверяет изменение. Агент пришёл поставить галочку, а в итоге запустил код. После этого сломанные PR стали редкостью. Осталась другая проблема — PR, которые работают, но не вписываются в дорожную карту.</p><h3>CI — стена, а не рекомендация</h3><p>Codecov и другие проверки настроены как required checks. Агент открывает PR, через несколько минут видит, что мёрж заблокирован, загружает навык с тестами и добивается покрытия. Никто не просил — инфраструктура сама направила агента.</p><h3>CLA (Contributor License Agreement) как детектор человека</h3><p>AutoGPT использует двойную лицензию, но Тиндл советует лицензионное соглашение участника (Contributor License Agreement, CLA) любому проекту, даже MIT. Подписание требует браузера и OAuth-потока GitHub на отдельном домене. Сегодня агенты с этим справляются плохо — и это хорошо, потому что большинство мейнтенеров не хотят, чтобы агент ходил в GitHub от их имени. Если CLA не подписан в течение недели, PR закрывается с комментарием «подпишите CLA и переоткройте».</p><h3>SHA коммита перед закрытием замечания</h3><p>Часть агентов закрывает все треды ревью, не исправляя код. В AutoGPT есть навык pr-address, который описывает допустимую последовательность: исправить, закоммитить, запушить, ответить, потом разрешить тред. Ответ должен содержать полный SHA коммита, полученный через git rev-parse HEAD, чтобы агент не подсунул старый хеш. Навык явно называет антипаттерны: «Acknowledged» — не исправление, и ссылка на коммит, который не трогает указанную строку, тоже не считается.</p><h2>Ворота, от которых пришлось отказаться</h2><p>Когда CI падает, AutoGPT изначально запускала агента, который читал лог и писал комментарий, что сломалось. Первая версия использовала Claude Code внутри GitHub Actions с аутентификацией в CI — лишняя широкая учётная запись. Потом перешли на Copilot внутри рабочего процесса: тот же результат, но без лишних кредов.</p><p>А потом выключили именно автоматические комментарии об ошибках CI. Прогоны в AutoGPT падают часто, и бот, который целыми днями описывает каждый падший прогон, создаёт не меньше шума, сколько и сами ошибки. Урок: оставляйте то, что снижает нагрузку на мейнтенера, и выключайте то, что превращается в фоновый шум.</p><h2>Четыре ловушки, которые стоит записать</h2><ul><li><b>Плохой AGENTS.md хуже, чем его отсутствие.</b> В AutoGPT сначала разбросали файлы повсюду и засорили контекст. Если поведение агентов ухудшилось — перечитайте, что вы написали.</li><li><b>GraphQL API GitHub быстро исчерпывает лимит запросов.</b> Если каждый инструмент в команде ходит в CLI как отдельный пользователь, лимит закончится. Создайте GitHub App и аутентифицируйте CLI через неё.</li><li><b>Сложное ревью стоит реальных денег.</b> У AutoGPT PR проходит через клон ветки, восемь агентов с разными ролями, запуск стека и скриншоты. Круто, но дорого. Сейчас эту процедуру запускают только для совсем маленьких или крупных PR.</li><li><b>Проверяйте авторизованные приложения.</b> Каждый протестированный инструмент оставляет OAuth-разрешение. Если перестали использовать приложение — удалите его из настроек GitHub.</li></ul><h2>Не всё в open source решается воротами</h2><p>Тиндл подчёркивает два тезиса, которые не связаны с инструментами. Первый: вы не обязаны принимать каждый пулреквест. Мёржить чужой LLM-вывод — асимметричная сделка: вы будете поддерживать этот код вечно. Закрыть PR и переписать решение самому — валидный выбор.</p><p>GitHub даёт мейнтенерам ручки: можно отключить пулреквесты целиком, ограничить создание issue только участникам с правами collaborator или требовать предварительного обсуждения. Если хотите — вообще закройте приём внешнего кода, как это сделали в SQLite: они принимают только баг-репорты. У вашего проекта тоже может быть своя граница.</p><p>Второй тезис: когда вы закрываете PR, но переписываете его идею сами, добавьте автора как соавтора, если это уместно. В AutoGPT 800 контрибьюторов, и один дополнительный соавтор ничего не стоит. Для большинства людей важно, что их проблему заметили и исправили.</p><h2>Выводы</h2><p>Open source развивался, делая сотрудничество явным: лицензии формализовали разрешения, issue сделали работу видимой, пулреквесты превратили ревью в общую практику. Инструкции для агентов в репозитории — следующий шаг в этом направлении. Правильная форма ещё не устоялась: у AutoGPT уже третья версия AGENTS.md, и она появилась потому, что команда сначала развёртывала плохие версии и смотрела, что с ними делают агенты.</p><p>Тиндл советует мейнтенерам записываться в программу обратной связи GitHub — maintainers.github.com:</p><blockquote>You've got to go there. You've got to sign up. It gets you all the connections you want at GitHub. That's where I learned about all this stuff, and where I share it.</blockquote><p>Мейнтенер всё ещё решает, что принимать, и задаёт планку. Разница лишь в том, что всё больше этого решения можно вынести рядом с кодом — туда, где уже находятся ваши контрибьюторы и их агенты.</p><p>Если тема интересна, загляните в разделы tproger: <a href="https://tproger.ru/tag/open-source">open source</a>, <a href="https://tproger.ru/tag/github">GitHub</a> и <a href="https://tproger.ru/tag/ai">искусственный интеллект</a>.</p><p><b>Источник:</b> <a href="https://github.blog/open-source/maintainers/your-contributors-are-ai-first-now-is-your-project/">GitHub Blog — Your contributors are AI-first now. Is your project?</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Zero на Three.js: инженерный разбор интерактивного сайта</title>
      <link>https://tproger.ru/articles/zero-na-three-js-inzhenernyj-razbor-interaktivnogo-sajta</link>
      <comments>https://tproger.ru/articles/zero-na-three-js-inzhenernyj-razbor-interaktivnogo-sajta?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/zero-na-three-js-inzhenernyj-razbor-interaktivnogo-sajta</guid>
      <description><![CDATA[<p>Разбираем, как BUNQ LABS сделала zero.university: виртуальный скролл, шейдеры, KTX2/DRACO, адаптивное качество и загрузка текстур без фризов. Проверьте техники.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/zero-na-three-js-inzhenernyj-razbor-interaktivnogo-sajta">Zero на Three.js: инженерный разбор интерактивного сайта</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Фреймворки и библиотеки]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 28 Jul 2026 04:19:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Представьте, что вы заходите на сайт, а он не пускает. Нет кнопки «войти» — только поле, где нужно нарисовать ноль. Как только круг замыкается, из линии расходится инеевый узор, и сайт начинает рассказывать историю. Это не декоративная заглушка: так работает <a href="https://zero.university/">zero.university</a> — кейс, где первый жест пользователя сразу задаёт тон всему повествованию.</p><p><b>Zero</b> — иммерсивный лендинг индийского стартапа Zero University, который предлагает альтернативу классическому университетскому пути. Сайт превращает скролл в шестиактную историю: традиционное образование, разбитое стекло, горящие деньги, уничтоженные сертификаты, тоннель из логотипа ZERO и, наконец, интерактивная карта города с офисами реальных компаний. Цель — не просто показать красивую картинку, а провести пользователя через аргумент: диплом перестаёт быть гарантией, а навыки открывают дорогу в индустрию.</p><p>Проект разрабатывала студия <b>BUNQ LABS</b> четыре месяца. Исходные ассеты занимали больше гигабайта: несжатые Blender-сцены, 8K-текстуры и запечённые анимации. Финальная сборка уместилась в 10 МБ и держит 60 FPS даже на бюджетном Android. Команда опубликовала технический разбор на Codrops, а мы выделили решения, которые можно перенести в свои WebGL-проекты.</p><ul><li>Первый жест как ворота: распознавание нуля — это простая проверка угла, округлости и замкнутости, а не нейросеть.</li><li>Виртуальный скролл заменяет нативный: одно число управляет загрузкой, анимацией, шейдерами и текстом.</li><li>Ассеты важнее рендера: DRACO, KTX2/ETC1S, атласы и собственный превьювер сжали гигабайты до мегабайтов.</li><li>Текстурные загрузки — главный источник фризов; декодинг в воркере и очередь по requestIdleCallback решают проблему.</li><li>Адаптивное качество по frame time позволяет не гадать о мощности устройства.</li><li>Мобильная точность шейдеров: mediump на Adreno и Mali — это реальный 16-битный float, и его недостаточно для сложных эффектов.</li></ul><p>Разбираем, как это устроено изнутри, и что из этого можно взять в свою работу.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-07-28/00e5f2ad-8fc7-49b9-88da-322a34a6bef1.webp" alt="Шесть этапов интерактивного повествования zero.university: от традиционного образования до интерактивной карты города." /><figcaption>Схема повествования: шесть этапов и пять «ворот», через которые проходит пользователь. Источник: Codrops / BUNQ LABS</figcaption></figure><h2>Сюжет из шести этапов и пяти ворот</h2><p>Интерактивный нарратив строится вокруг пяти «ворот» — точек, где скролл останавливается и ждёт действия пользователя. Сначала нужно нарисовать ноль, затем удерживать касание, чтобы разбить стекло, потом — чтобы запуститься сквозь тоннель. Каждый жест связан с сюжетным поворотом: обещание университетского пути буквально трескается, за ним появляется статистика безработицы, диплом превращается в горящую бумагу, сертификаты рвутся на полосы.</p><h3>От обещания к свободе</h3><p>Шесть этапов идут от иллюзии контролируемого пути к интерактивной карте, где пользователь сам выбирает, куда смотреть. Финальный экран — город с башней Zero University в центре. Можно приближать, панорамировать и открывать карточки ролей, сценариев и инструментов. Это не просто финал: после того как сайт вёл пользователя за руку, он отдаёт управление обратно.</p><h2>Архитектура: один скролл управляет всем</h2><p>Одно из главных архитектурных решений — отказ от нативного скролла браузера. Нет ScrollTrigger, нет огромного прокручиваемого DOM. Вместо этого события колёсика и тача обновляют виртуальное значение скролла, которое плавно догоняет цель. Всё остальное — загрузка ассетов, анимации, тайминги шейдеров, текст и оверлеи — читают это единственное число.</p><p>Проект разбит на девять таких сегментов; загрузчик одновременно выполняет роль первых ворот. Каждый сегмент самодостаточен: у него свой жизненный цикл, свои объекты и своя утилизация. Это упростило поддержку и отладку. Переход к любому этапу воспроизводит жизненные циклы всех предыдущих сегментов, поэтому состояние всегда консистентно — как будто пользователь дошёл до этого места естественным скроллом.</p><h2>Конвейер 3D-ассетов: как уместить 1 ГБ в 10 МБ</h2><p>Большую часть четырёх месяцев ушла не на код, а на подготовку ассетов. Исходники пришли из Blender: несжатая геометрия, 8K-текстуры, запечённые анимации — всё вместе больше гигабайта. Первый месяц команда тратила на то, чтобы выбрать формат для каждого типа ресурсов.</p><h3>DRACO и KTX2</h3><p>Вся геометрия отправляется со сжатием DRACO, декодеры для которого хостятся локально в public/vendor/. Урок команда усвоила на собственном опыте: замедление на gstatic и unpkg привело к тому, что все сжатые ассеты перестали декодироваться, хотя сами файлы лежали на ихних серверах. Если декодер зависит от чужого CDN, весь пайплайн зависит от него.</p><p>Самый большой выигрыш дали текстуры. PNG может быть маленьким на диске, но в видеопамять он загружается распакованным. Текстура 2048² занимает около 16 МБ VRAM независимо от размера файла. Формат KTX2 со сжатием ETC1S остаётся сжатым на GPU, занимает меньше памяти и загружается быстрее.</p><h3>Собственный превьювер компрессии</h3><p>С KTX2 есть проблема: ETC1S — lossy, и локально его не посмотреть обычным просмотрщиком. Чтобы не гадать с настройками, команда сделала внутренний дашборд: сжатая и несжатая версия каждой картинки и видео рядом. Так можно было подобрать степень сжатия индивидуально — усилить там, где артефакты не заметны, сохранить качество для ключевых объектов и отключить мипмапы, где они не нужны. Инструмент простой, но редко встречается в WebGL-пайплайнах, хотя экономит огромное количество времени и памяти.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-07-28/3a57d9e3-14e7-4f55-8aad-78cee34f1da3.webp" alt="Внутренний дашборд команды для сравнения сжатой и несжатой версии каждого ассета." /><figcaption>Превьювер компрессии: сжатая и несжатая версии текстур и видео рядом. Источник: Codrops / BUNQ LABS</figcaption></figure><h3>Атласы вместо дюжин картинок</h3><p>Похожие текстуры собрали в общие атласы. Например, все текстуры рук уместили в один атлас 4×4, а каждая mesh сдвигала UV-координаты, чтобы взять свой кусок. Спрайты текста, сертификаты, бумажные обрывки, облака, монеты и осколки стекла тоже ушли в атласы. Больше 50 отдельных изображений превратились примерно в дюжину атласов, а большинство градиентных фонов заменили несколькими строками GLSL. Ранние сборки весили 35–40 МБ, финальная — меньше 10 МБ. При этом интерактивная карта мира вынесена в отдельную группу загрузки, чтобы не блокировать открытие сайта.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-07-28/e1686dcf-b13a-494e-94b3-0e32f0eb880a.webp" alt="Атлас текстур рук 4×4: каждая mesh сдвигает UV-координаты, чтобы взять свой кусок." /><figcaption>Атлас рук: все текстуры рук упакованы в одну текстуру 4×4. Источник: Codrops / BUNQ LABS</figcaption></figure><h2>Текстурные загрузки: главный источник фризов</h2><p>Уменьшить скачивание — только половина дела. Сжатые текстуры всё равно нужно загрузить в GPU на главном потоке, и крупная текстура легко блокирует рендер на 50 мс — достаточно, чтобы потерять несколько кадров. Если эта загрузка случается впервые во время скролла, подёргивание заметно сразу. Сайт может хорошо показывать себя в бенчмарках и при этом чувствоваться вязким.</p><ul><li>Декодировать вне главного потока — через createImageBitmap(). Тогда во время рендера остаётся только загрузка в GPU.</li><li>Загружать в GPU в простое — декодированные текстуры ставят в очередь и выгружаются по requestIdleCallback, пока есть запас времени.</li><li>Разбивать большие атласы на плитки 256² и загружать по одной на кадр, чтобы не выбиваться из бюджета кадра.</li></ul><p>Когда известно, что следующий этап вот-вот появится — после загрузчика или во время перехода между воротами — очередь сбрасывается синхронно. Все нужные текстуры уже в видеопамяти, прежде чем они появятся на экране.</p><h2>Адаптивное качество по реальному frame time</h2><p>Невозможно заранее знать, на каком устройстве откроется сайт. Рендерер поэтому постоянно измеряет время кадра по скользящему окну и динамически меняет уровень качества. Если рендеринг замедляется — понижается tier. Если производительность стабильно высокая — tier снова растёт. Задержка между переключениями не даёт постоянно метаться у границы.</p><p>Уровни качества влияют только на визуальную полировку: devicePixelRatio, количество сэмплов размытия, геометрию монет, разрешение текста. Сама история остаётся неизменной и на флагмане, и на бюджетном телефоне. Для особо тяжёлых моментов — например, разбитие стекла — рендерер временно понижает пиксельное соотношение и отключает размытие и иней, прячет затраты внутри самого жеста.</p><h2>Шейдеры под каждый эффект</h2><p>Каждый ключевой момент использует собственный шейдер. ИИ помогал сгенерировать первые версии, но все финальные шейдеры переписывались и дорабатывались вручную. Вот несколько приёмов, которые стоит запомнить.</p><h3>Постпроцессинг из нескольких проходов</h3><p>Кадр собирается из цепочки проходов: основная 3D-сцена, процедурный фон, преломление стекла, иней и след от жеста, глубина резкости, foreground с зернистостью и тональной картой, отложенный текст, и в конце — разбитое стекло. Стеклянный, текстовый и шейдер разбития инициализируются лениво и прогреваются в простое, чтобы не попадать на критический путь загрузчика.</p><h3>Иней из нарисованного нуля</h3><p>Эффект инея строится на ping-pong-буфере из четырёх проходов: горизонталь, вертикаль и две диагонали. Каждый проход распространяет нарисованный штрих, захватывая самые яркие соседние пиксели и создавая восьмиугольный паттерн роста. Яркость текстуры льда модулирует шаг распространения, край получается кристаллическим. После замыкания контура центр масс штриха становится точкой отсчёта для радиального таяния.</p><h3>Освещение без источников света</h3><p>Реалтаймовое освещение скинненной меши рук было бы слишком дорогим, а свет должен был точно совпадать с оригинальным артом. Поэтому освещение запекли в текстуры и смешивают между ними. Используются два слота: один содержит текущий ключевой кадр, другой — следующий. По мере проигрывания анимации новый слот плавно вытесняет старый.</p><p>Перед смешиванием альфа предумножается, чтобы вокруг прозрачных краёв рук не появлялись тёмные ореолы. Обе текстуры освещения лежат в одном атласе, поэтому переключение между ключами требует только обновления двух UV-смещений и коэффициента смешивания.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-07-28/cb760b51-b0f4-4804-aff6-58ce015d2fda.webp" alt="Две запечённые текстуры освещения рук внутри одного атласа для плавного переключения." /><figcaption>Запечённое освещение рук: два ключевых кадра в одном атласе смешиваются по прогрессу анимации. Источник: Codrops / BUNQ LABS</figcaption></figure><h3>Горящие деньги</h3><p>Эффект сгорания использует noise-driven distance field. Фронт горения проходит по купюре, слегка опережая точку дискарда. Перед ним появляется тонкий HDR-ободок углей, затем обугленная поверхность. FBM — самая дорогая часть шейдера, поэтому фрагменты отсекаются как можно раньше, если они ещё не достигли порога горения.</p><h3>Рвущиеся сертификаты</h3><p>Каждая вершина хранит индекс полосы, к которой принадлежит фрагмент диплома. Фронт разрыва движется слева направо, и каждая полоса отделяется и падает независимо со своим вращением. Нормали для освещения строятся аналитически из той же волновой функции, которая анимирует меш, поэтому normal map не нужен.</p><h3>Тоннель ZERO</h3><p>Тоннель получается выдавливанием поперечного сечения из логотипа ZERO. Импульс яркости распространяется по мировой координате Z, поэтому эффект остаётся бесшовным, даже когда секции тоннеля повторяются.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-07-28/bfbebb17-dc15-4bbb-b00c-a1fb7f98c13f.webp" alt="Тоннель ZERO, полученный выдавливанием сечения логотипа." /><figcaption>Тоннель ZERO: импульс яркости распространяется по мировой координате Z, сохраняя бесшовность. Источник: Codrops / BUNQ LABS</figcaption></figure><h3>Текст, который приходит в фокус</h3><p>Нарративный текст появляется не мгновенно, а проявляется через семиотводное гексагональное размытие. Радиус размытия зависит от прогресса появления, поэтому буквы будто фокусируются. Проход выполняется в отложенном текстовом проходе и полностью пропускается, когда текст не виден.</p><h3>Точность на мобильных</h3><p>Одна из самых неприятных находок возникла на реальных мобильных GPU. Несколько шейдеров пришлось явно перевести на highp float, потому что на Adreno и Mali mediump — это настоящий 16-битный float. На десктопе ошибок не было, а на телефоне полосы диплома двигались одинаково, а эффект горения покрывался бэндами. Вывод: тестировать на реальных мобильных устройствах нужно с первых дней, а не перед релизом.</p><h2>Дизайн взаимодействий</h2><p>Исходный материал от заказчика состоял из картинок и видео, поэтому поведение каждых ворот проектировали с нуля. Большинство используют общую систему «нажми и удерживай». Общий конфиг задаёт момент появления подсказки и длительность удержания, а каждые ворота реализуют визуал через собственные хуки.</p><p>Пока пользователь удерживает касание, кадр постепенно смещается в тёмно-красный. Когда стекло разбивается, цвет возвращается примерно за 200 мс — это создаёт ощущение, что осколки выбивают темноту. Проверили вариант в 400 мс: он ощущался заметно слабее. Звук разбития привязан к первому отрисованному кадру, а не к таймеру: на медленных устройствах таймер может сыграть раньше анимации, и эффект рассинхронится.</p><h2>Финальная награда — интерактивный мир</h2><p>Последняя последовательность запуска выносит пользователя в открытое небо. Облака расступаются, открывая город с башней Zero University в центре, над которой появляется ореол — финальные ворота. Камера приземляется в интерактивную карту, где можно панорамировать, зумить и открывать карточки с ролями, сценариями и инструментами. Плашка Join Beta превращается в форму waitlist. После того как сайт вёл пользователя через историю, он отдаёт ему контроль — и просит остаться.</p><h2>FAQ</h2><h2>Выводы</h2><p>Самый важный урок проекта — подготовка ассетов и загрузка в GPU заслуживают не меньше внимания, чем сам рендеринг. Инструменты вроде превьювера компрессии, локальных декодеров, очереди загрузки и адаптивного менеджера качества редко выглядят героически, но именно они превратили гигабайт исходников в 10-мегабайтный сайт, который не тормозит на дешёвом телефоне.</p><blockquote>ИИ генерировала первые версии кода и прототип, который выиграл нам проект, за 48 часов. Но полировка — сотни мелких решений, тестирование на реальных устройствах и инженерная интуиция — осталась за людьми.</blockquote><p><b>Источник:</b> технический разбор команды BUNQ LABS на <a href="https://tympanus.net/codrops/2026/07/17/zero-the-engineering-behind-a-defiant-interactive-narrative/">Codrops</a>.<br />Попробуйте открыть <a href="https://zero.university/">zero.university</a> на десктопе и на бюджетном смартфоне — и сравните, где заметнее компромиссы качества.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как исправить hydration-ошибки RSC в Next.js: практический гид</title>
      <link>https://tproger.ru/articles/kak-ispravit-hydration-owibki-rsc-v-next-js-prakticheskij-gid</link>
      <comments>https://tproger.ru/articles/kak-ispravit-hydration-owibki-rsc-v-next-js-prakticheskij-gid?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ispravit-hydration-owibki-rsc-v-next-js-prakticheskij-gid</guid>
      <description><![CDATA[<p>Разбираем, почему возникают hydration mismatches в React Server Components и Next.js App Router, и как находить, исправлять и предотвращать их в production.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ispravit-hydration-owibki-rsc-v-next-js-prakticheskij-gid">Как исправить hydration-ошибки RSC в Next.js: практический гид</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 21 Jul 2026 11:37:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Hydration mismatch в Next.js легко поймать в next dev, но в production он превращается в минимизированный код ошибки React и ссылку на декодер. В статье разбираем, почему ошибки гидратации особенно болезненны в приложениях с React Server Components, какие причины встречаются чаще всего и какой рабочий процесс помогает находить и предотвращать такие баги.</p><p>Речь пойдёт не о теории reconcile-алгоритма, а о практике: что проверять первым делом, как изолировать проблему через Suspense, куда смотреть в production-логах и как писать тесты, которые ловят регрессии до попадания к пользователям.</p><h2>Что такое hydration mismatch</h2><p>Гидратация — это процесс, при котором React берёт серверный HTML и навешивает на него обработчики событий, после чего страница становится интерактивной. React ожидает, что дерево, которое он отрендерил на клиенте, точно совпадёт с тем, что пришло с сервера. Если строки, атрибуты или структура различаются, возникает hydration mismatch.</p><p>В development-режиме React покажет предупреждение, текст расхождения и стек компонента. В production всё сводится к коротким кодам вроде #418 или #425. Без телеметрии вы не узнаете, на каком маршруте, в каком компоненте и из-за каких данных произошёл сбой.</p><h2>Почему это больно именно в RSC</h2><p>В классическом SSR обычно один серверный рендер и один клиентский проход гидратации. App Router добавляет Server Components, Client Components, потоковую передачу, Suspense-границы, динамические данные маршрута и RSC payload, который едет рядом с HTML.</p><p>Когда несовпадение происходит вне Suspense-границы, React может отбросить весь серверный HTML и перерендерить дерево на клиенте. Приложение заплатило цену серверного рендера, а пользователь не получил ни производительности, ни преимуществ стриминга. Это не просто предупреждение в консоли, а реальная потеря производительности.</p><h2>Ключевые выводы</h2><ul><li>Hydration mismatch в production без телеметрии почти невозможно локализовать: нужна инструментация на клиенте.</li><li>Основные причины: browser-only API, дата/время/локаль, состояние авторизации, невалидный HTML, расширения браузера, CSS-in-JS.</li><li>Граница 'use client' — это не только маркер сборки, но и граница гидратации: серверный рендер должен быть детерминирован.</li><li>Suspense-границы помогают изолировать сбой и не давать ему сломать всё дерево.</li><li>Проверяйте hydration только на production-сборке: next dev ведёт себя иначе.</li><li>Добавьте Playwright-smoke-тесты на критичные маршруты, чтобы ловить регрессии в CI.</li></ul><h2>Самые частые причины</h2><p>Перед тем как копать RSC payload или сравнивать HTML, стоит проверить шесть типовых сценариев. Они покрывают подавляющее большинство production-инцидентов.</p><ul><li><b>Browser-only API во время рендера:</b> window, document, localStorage, navigator — сервер не знает об этих значениях.</li><li><b>Дата, время и локаль:</b> Date, Intl.DateTimeFormat, относительное время — сервер обычно в UTC, пользователь в своей зоне.</li><li><b>Состояние авторизации:</b> сервер рендерит выклогнутый UI, клиент сразу видит авторизованного пользователя.</li><li><b>Невалидный HTML:</b> браузер чинит DOM до гидратации, а React сравнивает с исходным деревом.</li><li><b>Расширения и middleware:</b> браузерные плагины и edge-переписывания меняют HTML.</li><li><b>CSS-in-JS и порядок классов:</b> styled-components и Emotion могут генерировать разные имена классов при стриминге.</li></ul><h3>Browser-only API и сторонние провайдеры</h3><p>Самая частая причина — не ваш собственный код, а сторонний провайдер, который читает localStorage, window или navigator при инициализации. Под это попадают аналитика, фичер-флаги, A/B-тесты, session replay и персонализация.</p><p>Проблемный вариант: провайдер читает localStorage прямо при рендере.</p><p>Исправление: серверные флаги передаются в Client Component, а локальные оверрайды применяются после гидратации.</p><p><b>Принцип:</b> серверный и первый клиентский рендер должны получить одинаковый результат. Браузерные оверрайды включаются в useEffect после монтирования.</p><h3>Дата, время и локаль</h3><p>Сервер часто работает в UTC, а пользователь — в своей зоне. Date.toLocaleString(), Intl.DateTimeFormat и функции вроде formatDistanceToNow() дают разные строки в зависимости от часового пояса и времени между рендером и гидратацией.</p><p>Проблемный вариант: относительное время считается на сервере.</p><p>Исправление: сервер отдаёт стабильную абсолютную дату, клиент заменяет её на относительную после монтирования.</p><p>suppressHydrationWarning здесь уместен, потому что расхождение ожидаемо, ограничено листовым элементом  и управляется явно. Но оборачивать им большой контейнер, чтобы скрыть неизвестную ошибку, — значит замазать баг, а не починить его.</p><h3>Состояние авторизации</h3><p>Типичный сценарий: сервер рендерит UI для неавторизованного пользователя, потому что маршрут статический или не читает куки, а клиент сразу видит залогиненного пользователя. При каждой загрузке страница перерендеривается.</p><p>Решение — читать куки на сервере через cookies() из next/headers.</p><p>С cookies() маршрут становится динамическим — это обычно правильный компромисс для UI, зависящего от авторизации. Начиная с Next.js 15, cookies() асинхронный и требует await. В Next.js 16 синхронный доступ к request-time API полностью уходит.</p><h3>Невалидный HTML</h3><p>Браузер молча чинит невалидную вёрстку. React же гидратирует не исходную строку, а DOM, который построил браузер. Классический пример —  внутри <p></p>: браузер закроет параграф до div, и React увидит другое дерево.</p><p>Браузер превратит это в примерно такую структуру:</p><p>Если HTML приходит из CMS или редактора, валидируйте и нормализуйте его на сервере. Для собственных компонентов следите за предупреждениями validateDOMNesting в DevTools.</p><h3>Расширения браузера и middleware</h3><p>Менеджеры паролей, переводчики, грамматические плагины и другие расширения могут вставлять или переупорядочивать узлы до гидратации. Edge middleware и CDN-трансформации тоже могут переписывать HTML в пути.</p><p>Если несовпадение затрагивает только &lt;html&gt; или &lt;body&gt;, сначала подозревайте расширения. React 19 стал терпимее к инъекциям в head/body, но на старых версиях такие ошибки особенно шумные.</p><p>suppressHydrationWarning на корневых элементах — документированный escape hatch, но он работает только на один уровень вглубь и не должен использоваться для сокрытия неизвестных расхождений в продуктовом UI.</p><h3>CSS-in-JS и порядок классов</h3><p>В проектах со styled-components или Emotion имена классов могут зависеть от порядка рендера. Стриминг и Suspense меняют этот порядок, поэтому нужен registry с useServerInsertedHTML.</p><h2>Production workflow для отладки</h2><p>Не начинайте с diff-а огромных HTML-документов. Работайте по порядку, сужая область поиска.</p><ol><li>Настройте клиентскую телеметрию: перехватывайте console.error и отправляйте hydration-ошибки на свой endpoint.</li><li>Воспроизводите баг на production-сборке: next build &amp;&amp; next start, а не next dev.</li><li>Используйте Suspense-границы, чтобы изолировать проблемный участок и понять, в каком поддереве ошибка.</li><li>Сравнивайте серверный HTML (curl) и клиентский DOM после гидратации.</li><li>Когда найдёте расходящийся элемент, проверьте: browser-only API, дата/время, авторизацию, HTML-вложенность, расширения, стили.</li></ol><h3>Инструментация: instrumentation-client.ts</h3><p>Файл instrumentation-client.ts в Next.js запускается после загрузки документа, но до гидратации React. Это удобная точка для лёгкой клиентской телеметрии.</p><p>Если используете LogRocket, Sentry или другой инструмент, прикрепите тот же payload к текущей сессии — тогда можно будет увидеть, как выглядела страница в момент сбоя.</p><h3>Изоляция через Suspense</h3><p>Suspense-границы не только показывают fallback при загрузке. Если hydration mismatch случается внутри границы, React может ограничить восстановление этим поддеревом, а не переключать весь root на клиентский рендер.</p><p>Оберните поочерёдно основные секции. Если страничная ошибка исчезает после оборачивания конкретной секции, баг почти наверняка внутри неё.</p><h3>Сравнение HTML и DOM</h3><p>Получите серверный HTML через curl с нужными куками:</p><p>В Chrome DevTools после загрузки страницы скопируйте живой DOM:</p><p>Сохраните результат в /tmp/client-render.html и сравните:</p><p>Этот метод хорошо ловит HTML-слой, но может пропустить расхождения на уровне reconciler-а и RSC payload. Если raw HTML совпадает, проверяйте Client Components и браузерное состояние.</p><h2>Профилактика</h2><p>Лучше не допускать ошибки, чем потом их чинить. Несколько практик, которые помогают держать серверный и клиентский рендер синхронизированными.</p><ol><li>Server Component должен быть детерминированным: одни и те же входные данные дают одни и те же выходные HTML и RSC payload.</li><li>Все browser-only API, куки, заголовки, локаль и время должны оставаться за границей 'use client' или читаться через request-time API на сервере.</li><li>Для клиентского UI сначала рендерите стабильный baseline, а улучшения добавляйте в useEffect после гидратации.</li><li>Валидируйте HTML из внешних источников до того, как React его увидит.</li><li>Тестируйте hydration на production-сборке в CI.</li></ol><h3>Пример: стабильный baseline + клиентское улучшение</h3><p>Скелетон — это стабильная базовая разметка. Карточки товаров появляются после монтирования. Сервер и первый клиентский рендер совпадают, а пользователь получает нужный контент чуть позже.</p><h3>Smoke-тесты в CI</h3><p>Ручное тестирование пропустит регрессии. Добавьте Playwright-тест на критичные маршруты.</p><p>Запускайте такой тест только против production-сборки, никогда против next dev. Сделайте его обязательной проверкой для маршрутов, где hydration failure критичен: продуктовые страницы, дашборды, чекаут, личный кабинет.</p><h2>FAQ</h2><h2>Выводы</h2><p>Hydration mismatch — не придирка React, а реальный баг производительности и корректности. Каждое несовпадение означает, что приложение могло отбросить серверный HTML, за который уже заплатило ресурсами.</p><p>В приложениях с React Server Components, где цель — меньше клиентского JavaScript и более ранний стриминг полезного UI, hydration failures тихо отменяют эти преимущества. Поэтому важно держать браузерную логику за границей 'use client', рендерить стабильные серверные baseline и проверять гидратацию в production-условиях до того, как ошибку найдут пользователи.</p><blockquote>Hydration errors should be fixed, not ignored — официальная позиция React.</blockquote><p><b>Источник:</b> <a href="https://blog.logrocket.com/how-fix-rsc-hydration-mismatches-next-js/" rel="noopener noreferrer">LogRocket Blog — How to fix RSC hydration mismatches in Next.js</a>.</p><p>Проверьте свои критичные маршруты на production-сборке, настройте телеметрию и не давайте hydration-ошибкам копиться в консоли.</p>]]></content:encoded>
    </item>
    <item>
      <title>Метаклассы в Python: как работает фабрика классов</title>
      <link>https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov</link>
      <comments>https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov</guid>
      <description><![CDATA[<p>Разбираем, что такое метаклассы в Python, как type создаёт классы и когда стоит писать свой метакласс. Примеры кода и практические рекомендации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/metaklassy-v-python-kak-rabotaet-fabrika-klassov">Метаклассы в Python: как работает фабрика классов</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Объектно-ориентированное программирование]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 09 Jul 2026 12:12:32 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если класс в Python — это шаблон, по которому создаются объекты, то метакласс — это шаблон, по которому создаются сами классы. Идея звучит рекурсивно, но именно в ней скрывается ответ на вопрос, почему в Python всё — объект: даже определение класса можно собрать, изменить или зарегистрировать программно.</p><p>В этой статье разберём, как внутри устроены метаклассы, зачем нужен встроенный type с тремя аргументами и когда стоит писать свой метакласс, а когда — обойтись наследованием или декоратором.</p><h2>Класс — тоже объект</h2><p>В Python 3 любой класс является экземпляром метакласса. По умолчанию этим метаклассом выступает type. Поэтому запись class Foo: pass интерпретатор превращает примерно в такой вызов:</p><p>С точки зрения языка разницы почти нет: и в том, и в другом случае получается объект класса Foo, у которого есть атрибуты, методы и собственный тип. Проверить это можно прямо в интерпретаторе:</p><p>type сам является экземпляром себя — это одна из тех особенностей Python, которые сначала удивляют, а потом помогают понять, что классы и объекты в языке живут по одним правилам.</p><p>Метакласс — это класс, экземплярами которого являются другие классы.</p><p>В Python 3 по умолчанию метаклассом любого класса является type.</p><p>Вызов type(name, bases, dict) создаёт класс динамически.</p><p>Собственный метакласс наследуется от type и переопределяет __new__ или __init__.</p><p>Чаще всего задачу решают наследованием или декоратором класса — метакласс нужен редко.</p><h2>type — не только функция проверки типа</h2><p>Всем знаком вызов type(x), который возвращает тип объекта. Но если передать type три аргумента, он превращается в фабрику классов:</p><ul><li>первый аргумент — имя класса (становится __name__);</li><li>второй — кортеж базовых классов (становится __bases__);</li><li>третий — словарь пространства имён (становится __dict__).</li></ul><p>Этот механизм лежит в основе любого определения класса. Когда Python видит ключевое слово class, он сначала собирает тело класса в словарь, а затем вызывает метакласс, чтобы создать сам класс.</p><p>Пример — класс с методом, собранный вручную:</p><p>Такой код редко пишут в production, но он наглядно показывает, что класс — это всего лишь объект, созданный вызовом фабрики. А значит, эту фабрику можно заменить на свою.</p><h2>Как написать свой метакласс</h2><p>Чтобы изменить процесс создания класса, наследуемся от type и переопределяем __new__. Этот метод отвечает за создание самого класса, поэтому в нём можно добавить общие атрибуты, проверить имя или зарегистрировать класс в каталоге.</p><p>Здесь AutoAttrMeta автоматически добавляет атрибут version каждому классу, который её использует. То же самое можно сделать через наследование, но метакласс действует на этапе создания класса, а не при вызове методов.</p><p>Помимо __new__, часто переопределяют __init__ метакласса: он получает уже созданный класс и может его донастроить. А __call__ контролирует создание экземпляров класса — именно он вызывает __new__ и __init__ объекта.</p><h2>А точно нужен метакласс?</h2><p>Тим Петерс, автор «Дзена Python», как-то сказал, что метаклассы — это магия, которую 99% разработчиков никогда не понадобится. Если вы сомневаетесь, нужны ли они вам, — скорее всего, не нужны.</p><blockquote>Метаклассы — это более глубокая магия, чем та, о которой 99% пользователей должны беспокоиться. Если вы сомневаетесь, нужны ли они вам, значит, не нужны.</blockquote><p>Для сравнения — три способа дать классам общий атрибут:</p><h3>Наследование</h3><h3>Декоратор класса</h3><h3>Метакласс</h3><p>Наследование и декоратор читаются проще и не лезут в механику создания класса. Метакласс выигрывает, когда поведение должно быть неизбежным для всех наследников или когда логика завязана на сам процесс построения класса.</p><p><b>Когда метакласс действительно уместен:</b><br />— автоматическая регистрация подклассов в плагиновой системе;<br />— валидация объявлений полей (например, ORM проверяют типы атрибутов);<br />— принудительный единый API для большого семейства классов;<br />— реализация паттернов вроде Singleton на уровне класса.</p><h2>Практический пример: автоматическая регистрация плагинов</h2><p>Представьте, что вы пишете расширяемую систему: каждый новый адаптер должен попадать в общий реестр. Метакласс может добавлять класс в реестр сразу при определении, без ручного вызова регистрации.</p><p>Такой подход удобен, потому что автор плагина просто объявляет класс, а реестр обновляется сам. В реальных фреймворках — например, в старых версиях Django — похожая идея используется для построения моделей данных.</p><h2>Выводы</h2><p>Метаклассы — не ежедневный инструмент, но понимание их работы делает код Python прозрачнее. Вы начинаете видеть, что class — это тоже объект, а type — всего лишь фабрика, которую при желании можно заменить.</p><p>Главное правило: если задачу решает наследование или декоратор класса, не тащите метакласс. А если без контроля за созданием класса никуда — смело берите type и наследуйтесь от него.</p><p>Источник и дополнительное чтение: <a href="https://realpython.com/python-metaclasses/">Python Metaclasses</a> на Real Python.</p>]]></content:encoded>
    </item>
    <item>
      <title>Claude Code научился работать в циклах: разбираем официальный гайд Anthropic</title>
      <link>https://tproger.ru/articles/claude-code-nauchilsya-rabotat-v-ciklah-razbiraem-oficialnyj-ga</link>
      <comments>https://tproger.ru/articles/claude-code-nauchilsya-rabotat-v-ciklah-razbiraem-oficialnyj-ga?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/claude-code-nauchilsya-rabotat-v-ciklah-razbiraem-oficialnyj-ga</guid>
      <description><![CDATA[<p>Anthropic опубликовала официальный гайд по loops в Claude Code. Разбираем turn-based, goal-based, time-based и proactive циклы, примеры команд и советы по экономии токенов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/claude-code-nauchilsya-rabotat-v-ciklah-razbiraem-oficialnyj-ga">Claude Code научился работать в циклах: разбираем официальный гайд Anthropic</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Методологии разработки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 07 Jul 2026 11:41:33 GMT</pubDate>
      <content:encoded><![CDATA[<p>Пока одни разработчики всё ещё соревнуются в длине промптов, в Anthropic считают, что главный навык будущего — не prompt-инжиниринг, а проектирование циклов. В начале июля 2026 года официальный аккаунт <b>ClaudeDevs</b> опубликовал гайд <a href="https://claude.com/blog/getting-started-with-loops">Getting started with loops</a>, в котором команда Claude Code наконец дала общую терминологию тому, что последние недели обсуждали в X Борис Черни, Питер Штайнбергер и Эдди Османи.</p><p>В статье loops определяются просто: <b>агент повторяет циклы работы до тех пор, пока не выполнится условие остановки</b>. Разница между типами циклов Anthropic выводит из четырёх вещей: что запускает цикл, что его останавливает, какая примитивная команда Claude Code используется и для каких задач это подходит.</p><h2>Что такое loop в Claude Code</h2><p>Если коротко, loop — это способ переложить на агента не отдельную команду, а <b>повторяющийся процесс</b>. Сначала агент получает или собирает контекст, потом действует, проверяет результат и либо останавливается, либо начинает следующую итерацию. Человек при этом отвечает не за каждый шаг, а за то, чтобы правильно описать условие остановки, проверку качества и триггер запуска.</p><p>Для российских разработчиков, которые часто запускают Claude Code на удалённом сервере по SSH, это особенно удобно: можно настроить цикл, который работает, пока вы спите, и присылает отчёт утром в Telegram или Slack. Главное — не забыть про лимиты токенов, потому что неограниченный цикл способен съесть месячный бюджет за одну ночь.</p><p>Claude Code предлагает четыре типа loops: turn-based, goal-based, time-based и proactive.</p><p>Каждый цикл характеризуется триггером, условием остановки и подходящей командой: ручной prompt, /goal, /loop//schedule или автоматическая рутина.</p><p>Качество результата зависит от проверочных навыков (SKILL.md) и чистоты кодовой базы, а не только от самого цикла.</p><p>Чтобы не переплачивать за токены, важны чёткие критерии завершения, ограничение по числу итераций и правильный выбор модели.</p><p>Начинать стоит с простейшего цикла и добавлять сложность только там, где ручная работа реально становится узким местом.</p><h2>Четыре типа циклов</h2><h3>Turn-based: ручной цикл</h3><p>Самый простой и самый знакомый вид. Пользователь отправляет prompt, Claude собирает контекст, вносит правки, запускает тесты и возвращает результат. После этого человек проверяет работу и пишет следующий prompt. Каждый такой оборот — это один turn.</p><p>По мнению команды Claude Code, здесь ключевой рычаг — не более длинный prompt, а <b>проверочный SKILL.md</b>. Если вы обычно вручную открываете dev-сервер, кликаете по новой кнопке, проверяете консоль и запускаете Lighthouse, эти шаги стоит записать в skill. Чем более количественные проверки вы зададите, тем чаще Claude сможет сам понять, что задача выполнена.</p><h3>Goal-based: цикл с целью через /goal</h3><p>Когда задача сложная и одного оборота недостаточно, помогает команда /goal. Вы явно описываете критерий успеха и максимальное число попыток. Каждый раз, когда Claude хочет остановиться, оценочная модель проверяет условие: если цель не достигнута — агент возвращается к работе.</p><p>Чем более детерминирован критерий, тем лучше. «Сделай хорошо» — плохая цель. «Добейся Lighthouse Performance ≥ 90, не более 5 попыток» — хорошая. То же самое работает для числа пройденных тестов, покрытия кода или отсутствия ошибок линтера.</p><h3>Time-based: цикл по расписанию /loop и /schedule</h3><p>Некоторые задачи не требуют вашего присутствия: утренняя сводка по Slack, проверка PR на ревью, мониторинг CI. Для таких случаев есть /loop: команда повторяет prompt через заданный интервал, пока вы её не отмените или пока работа не закончится.</p><p>/loop работает на вашем компьютере, поэтому при выключении терминала цикл остановится. Если нужно, чтобы агент работал в облаке даже с закрытым ноутбуком, используется /schedule — эта команда создаёт рутину, которая запускается по расписанию на серверах Anthropic (research preview).</p><h3>Proactive: проактивные рутины</h3><p>Проактивные циклы объединяют всё вышеперечисленное: /schedule для запуска по событию или расписанию, /goal для критерия готовности, skills для проверки, dynamic workflows для параллельной обработки и auto mode, чтобы цикл не останавливался на каждом разрешении.</p><p>Типичный сценарий: обработка входящих баг-репортов. Рутина каждый час проверяет канал обратной связи, триажирует каждую заявку, чинит баг, прогоняет тесты и отвечает пользователю. Для сложных случаев можно параллельно исследовать несколько решений в разных worktrees и поручить «судейскому» агенту выбрать лучшее.</p><h2>Как не потерять качество кода</h2><p>Автономность без контроля качества быстро превращается в генерацию мусора. В гайде Anthropic выделяет четыре опоры, на которых держится качество loop:</p><ul><li><b>Чистая кодовая база.</b> Claude копирует паттерны, которые уже есть в проекте. Если в репозитории хаос, агент будет его множить.</li><li><b>Проверочные skills.</b> SKILL.md должен содержать конкретные шаги, инструменты и критерии, по которым Claude сам оценивает результат.</li><li><b>Актуальная документация.</b> Фреймворки и библиотеки меняются, и агенту нужны свежие best practices.</li><li><b>Второй агент для ревью.</b> Проверяющий со свежим контекстом менее предвзят, чем основной агент. Можно использовать встроенный /code-review или Code Review for GitHub.</li></ul><p>Важный совет: когда отдельный результат не дотягивает до стандарта, не исправляйте только конкретный случай — закодируйте правило в skill или CLAUDE.md, чтобы все будущие итерации работали лучше.</p><h2>Как не сжечь бюджет на токенах</h2><p>Loops — это не бесплатная автоматизация. Каждый оборот стоит денег, а неосторожная proactive-рутина может породить сотни параллельных подагентов. В гайде перечислены шесть способов держать расходы под контролем:</p><ul><li>Выбирайте подходящий примитив и модель: мелкие задачи не нуждаются в сложных оркестрациях.</li><li>Формулируйте чёткие критерии завершения: конкретнее цель — меньше лишних итераций.</li><li>Запускайте пилот на малой выборке перед массовым прогоном.</li><li>Используйте скрипты для детерминированной работы: запуск готового скрипта дешевле, чем рассуждение модели.</li><li>Не запускайте рутины чаще, чем меняется объект мониторинга.</li><li>Регулярно смотрите /usage, /goal без аргументов и /workflows, чтобы видеть, куда уходят токены.</li></ul><h2>С чего начать</h2><p>Авторы гайда предлагают не начинать с proactive-рутин, а посмотреть на свою повседневную работу и найти одно место, где вы сами являетесь узким звеном. Задайте три вопроса:</p><ul><li>Могу ли я описать проверку результата так, чтобы Claude мог сам её выполнить?</li><li>Достаточно ли чётко я понимаю, что значит «готово»?</li><li>Эта работа приходит по расписанию или в ответ на внешние события?</li></ul><p>Если ответ на первый вопрос «да» — начните с turn-based цикла и проверочного skill. Если на второй — попробуйте /goal. Если на третий — /loop или /schedule. Запустите цикл, понаблюдайте, где он застревает или перегибает палку, и дорабатывайте harness, а не только prompt.</p><h2>Сводка: какой цикл когда использовать</h2><h2>FAQ</h2><h2>Выводы</h2><p>Гайд Anthropic — не просто описание четырёх команд. Это попытка дать разработчикам общий язык для обсуждения того, как ИИ-агенты переходят из разряда «помощников в чате» в разряд «автономных рабочих процессов». Turn-based, goal-based, time-based и proactive loops — это не конкуренты, а ступени одной эволюции: от ручного управления каждым шагом к проектированию систем, которые управляют сами собой.</p><p>Главная метафора, которую стоит уносить с собой: ваш вклад перестаёт измеряться качеством очередного prompt, а начинает измеряться качеством <b>harness</b> — системы проверок, остановок и триггеров. Если вы ещё не пробовали /goal или /loop, начните с одной повторяющейся задачи на этой неделе. Скорее всего, вы удивитесь, как много ручной работы можно отдать циклу.</p><blockquote>I don't prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops.</blockquote><p><b>Источники:</b></p><ul><li><a href="https://claude.com/blog/getting-started-with-loops">Getting started with loops — официальный гайд Anthropic</a></li><li><a href="https://x.com/ClaudeDevs/status/2074208949205881033">@ClaudeDevs on X</a></li><li><a href="https://addyosmani.com/blog/loop-engineering/">Loop Engineering — Addy Osmani</a></li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Как усыпить Python: полный гид по time.sleep(), asyncio.sleep() и паузам в потоках</title>
      <link>https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep</link>
      <comments>https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep</guid>
      <description><![CDATA[<p>Разбираем, как добавлять задержки в Python: от time.sleep() до asyncio.sleep(), Event.wait() и GUI-методов вроде Tkinter.after(). С примерами кода и ловушками.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-usypit-python-polnyj-gid-po-time-sleep-asyncio-sleep">Как усыпить Python: полный гид по time.sleep(), asyncio.sleep() и паузам в потоках</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 25 Jun 2026 16:32:37 GMT</pubDate>
      <content:encoded><![CDATA[<p>Иногда Python должен подождать. Задержка между запросами к API, пауза перед повторной проверкой сервера, ритмичный вывод в терминал — за всё это отвечает функция time.sleep(). Она кажется элементарной, но в разных контекстах — синхронном коде, потоках, асинхронности и GUI — пауза устроена по-разному. Разбираем, как не «усыпить» приложение.</p><h2>Что делает time.sleep()</h2><p>Функция time.sleep(seconds) приостанавливает выполнение текущего потока на указанное время. Аргумент — число секунд, включая дробные значения. Например, 0.001 — это одна миллисекунда, а 1.5 — полторы секунды.</p><p>Важный нюанс: time.sleep() гарантирует лишь минимальную задержку. Реальная пауза почти всегда чуть дольше из-за планировщика операционной системы и текущей нагрузки. Для большинства задач это не критично, но для высокоточного тайминга лучше искать другие инструменты.</p><h2>Типичные сценарии использования</h2><p>Паузы чаще всего нужны в трёх случаях: чтобы не перегружать внешний сервис, чтобы дать интерфейсу время прогрузиться и чтобы сделать вывод в консоли более читаемым.</p><ul><li>Распределение запросов к API с ограничением rate limit.</li><li>Повторные попытки выполнения ненадёжных операций.</li><li>Пауза между итерациями мониторинга или парсинга.</li></ul><h3>Мониторинг сайта с интервалом в 60 секунд</h3><p>Простой аптайм-бот опрашивает страницу и засыпает на минуту. Без задержки скрипт уйдёт в бесконечный цикл и может получить бан по IP.</p><p><b>Совет:</b><br />Для реального мониторинга добавьте логирование или уведомления, иначе ошибки останутся только в консоли.</p><h2>Декоратор для повторных попыток</h2><p>Ненадёжные операции — загрузка файла, запрос к внешнему API — иногда падают из-за временной проблемы. Вместо того чтобы оборачивать каждый вызов в цикл, удобно сделать декоратор, который повторяет функцию с задержкой.</p><p>Обратите внимание на raise внутри блока except: если его убрать, исключение не пойдёт наружу, и декоратор не поймёт, что нужна повторная попытка.</p><h2>Паузы в потоках: Event.wait()</h2><p>В потоках можно использовать time.sleep(), но он непрерываен: поток не сможет быстро завершиться, пока спит. Для корректной остановки лучше применять threading.Event().wait(). Этот метод прерывается, как только событие установлено через event.set().</p><p>Такой подход позволяет аккуратно завершить все потоки по Ctrl+C: event.set() мгновенно будит все ожидающие wait().</p><h2>Неблокирующие паузы в asyncio</h2><p>Асинхронный код в Python появился в версии 3.4 модуля asyncio и получил синтаксис async/await в 3.5. Внутри корутин нельзя использовать обычный time.sleep(): он заморозит весь event loop. Для паузы в асинхронном коде есть asyncio.sleep().</p><p>Ключевое отличие: asyncio.sleep() приостанавливает только текущую корутину, уступая управление event loop. Остальные задачи продолжают выполняться. Это особенно важно при соблюдении rate limit при параллельных запросах.</p><h2>Как не заморозить GUI</h2><p>В GUI-фреймворках весь интерфейс работает в главном потоке с собственным циклом событий. Если вызвать time.sleep() в обработчике кнопки, окно повиснет: не будет отрисовки, не сработают клики, а в Windows может появиться предупреждение о неответствии.</p><p>Метод .after() в Tkinter планирует callback через заданное число миллисекунд и сразу возвращает управление циклу событий. Аналог в wxPython — wx.CallLater(). Паттерн универсален: не блокируйте поток, планируйте отложенный вызов.</p><ul><li>time.sleep() — универсальный способ поставить синхронную паузу, но он блокирует поток целиком.</li><li>Для повторных попыток удобен декоратор с задержкой, который ловит исключения и делает retry.</li><li>В потоках предпочитайте Event.wait(): его можно прервать через event.set().</li><li>В асинхронном коде используйте asyncio.sleep(), иначе заморозите весь event loop.</li><li>В GUI применяйте фреймворковые методы вроде Tkinter.after(), чтобы интерфейс оставался отзывчивым.</li></ul><h2>Выводы</h2><p>Пауза в коде — это не просто «подождать секунду». В синхронном скрипте достаточно time.sleep(), в потоке важно уметь выходить по сигналу, в асинхронном коде нельзя блокировать event loop, а в GUI нужно работать в рамках фреймворкового цикла событий. Правильный инструмент зависит от контекста, и выбор влияет на отзывчивость и стабильность приложения.</p><blockquote>Не блокируйте поток без причины. Если можно уступить управление — уступите.</blockquote><p>Источник и дополнительные материалы: <a href="https://realpython.com/python-sleep/">Real Python — Python sleep(): How to Add Time Delays to Your Code</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что такое Git worktrees и зачем их использовать</title>
      <link>https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat</link>
      <comments>https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat</guid>
      <description><![CDATA[<p>Git worktrees появились ещё в 2015 году, но популярность обрели только недавно. Разбираем, что это такое, как использовать в терминале и зачем они пригодятся.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-takoe-git-worktrees-i-zachem-ih-ispolzovat">Что такое Git worktrees и зачем их использовать</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Инструменты командной строки]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 18 Jun 2026 03:00:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>Перевод статьи Cassidy Williams из GitHub Blog, оригинал: <a href="https://github.blog/ai-and-ml/github-copilot/what-are-git-worktrees-and-why-should-i-use-them/">https://github.blog/ai-and-ml/github-copilot/what-are-git-worktrees-and-why-should-i-use-them/</a></p><p>Git worktrees позволяют держать несколько рабочих копий одного репозитория на разных ветках — и сейчас эта возможность в тренде. Забавно, что в Git она появилась ещё в 2015 году. Но worktrees действительно удобны, и в этой статье разберёмся, зачем они нужны, чем отличаются от обычных веток и почему внезапно стали популярны.</p><p>worktree — это дополнительная рабочая копия репозитория на другой ветке, привязанная к тому же .git.</p><p>С их помощью можно переключаться между задачами, не прерывая текущую работу и не используя stash.</p><p>Они особенно полезны при параллельной работе с ИИ-агентами и в приложении GitHub Copilot.</p><p>У worktrees есть ограничения: раздувание зависимостей, необходимость убирать папки, правила .gitignore и запрет на одновременный checkout одной ветки в нескольких worktrees.</p><h2>Переключение контекста через ветки и stash</h2><p>Представьте: вы работаете над задачей и вдруг получаете срочный баг. Нужно срочно переключить контекст.</p><p>Сначала, скорее всего, спрячете текущие изменения в stash:</p><p>Потом перейдёте на main и обновите её:</p><p>Затем создадите ветку с хотфиксом:</p><p>Пофиксите, закоммитите и запушите ветку:</p><p>После слияния pull request вы вернётесь к компьютеру, подтянете main и удалите ветку с багфиксом:</p><p>А потом сможете вернуться к задаче, над которой работали:</p><p>Фух. На чём мы остановились?</p><p>Переключение туда-сюда, перезагрузка файлов, переустановка node_modules в зависимости от того, что изменилось, — всё это отнимает много сил. Нагрузка от смены контекста серьёзная.</p><p>Это базовый пример, но иногда разработчики справлялись с таким хаосом сложными командами git stash или даже несколькими клонами одного репозитория (я сама грешила этим).</p><p>А потом появились… worktrees!</p><h2>Переключение контекста через worktrees</h2><p>С worktrees вы никогда не покидаете свою ветку и не используете stash, а редактор с вашей текущей фичей остаётся нетронутым.</p><p>Эта команда мгновенно создаёт соседнюю папку hotfix-workspace, базирует её на main и создаёт новую ветку hotfix-bug.</p><p>Теперь можно открыть эту папку в новом окне редактора (или перейти в неё через cd) и чинить баг. Исходное окно редактора остаётся в том же состоянии, в котором вы его оставили.</p><p>Pull request сливаете онлайн, как обычно, а после слияния можно просто удалить временную папку.</p><p>Гораздо плавнее! Нет риска конфликтов stash, редактор не перезагружается, и вы действительно можете работать параллельно.</p><h2>Так почему же сейчас?</h2><p>Долгое время worktrees были относительно неизвестны. Большинство разработчиков никогда о них не слышали, потому что либо Git GUI не поддерживали их (или относились как к второсортной функции), либо все привыкли к знакомой схеме: feature-ветка, работа, PR, merge и повтор.</p><p>Сейчас наша работа изменилась. ИИ заставляет нас работать параллельно больше, чем когда-либо в истории разработки ПО. Разработчики запускают множество сессий одновременно, а «культура код-ревью» растёт быстрее, чем «культура написания кода».</p><p>Агенты и люди могут делать больше параллельно с помощью worktrees. Это режим по умолчанию в приложении GitHub Copilot и во многих других современных инструментах.</p><blockquote>Агенты и люди могут делать больше параллельно с помощью worktrees.</blockquote><h2>В чём подвох?</h2><p>Worktrees решают кучу проблем, но есть нюансы, на которые стоит обращать внимание.</p><ul><li>Раздувание зависимостей: каждая папка worktree требует собственную копию зависимостей проекта. Если запускать npm install или pip install в нескольких worktrees, диск может быстро закончиться.</li><li>Управление папками: нужно удалять папки worktree, чтобы со временем не засорять родительский каталог. Приложение GitHub Copilot часто делает это за вас, но если вы работаете в терминале, придётся следить самостоятельно.</li><li>Требования к глобальному .gitignore: если создавать worktree внутри основного репозитория, их нужно вручную добавить в .gitignore, чтобы случайно не закоммитить. Можно создавать worktree за пределами основного репозитория (GitHub Copilot делает это по умолчанию), но это стоит учитывать.</li><li>Ограничение «одна ветка»: Git не позволяет одновременно checkout’ить одну и ту же ветку в двух разных worktrees, чтобы избежать повреждения данных.</li></ul><h2>Как использовать git worktrees в приложении GitHub Copilot?</h2><p>Отличный вопрос! Здорово, что там всё работает «из коробки». Когда вы открываете приложение, на главном экране есть выпадающий список, который спрашивает, где запускать новую сессию. По умолчанию выбран новый worktree.</p><p>Когда вы запускаете новую сессию, можно нажать на имя сессии вверху приложения и увидеть (забавное!) сгенерированное имя вашего worktree, а также путь, где он находится, проект, для которого он создан, и сведения о внесённых изменениях.</p><p>Проще простого!</p><h2>Стоит ли использовать worktrees?</h2><p>Я дам вам самый senior-ответ, который только можно: зависит от ситуации! Вам может быть удобнее работать по-другому. Возможно, вы не так много работаете параллельно и привыкли к ментальной модели веток и stash. Возможно, теперь вы будете использовать только worktrees. А может, захотите и то, и другое!</p><p>Мир у ваших ног, и попробовать всё это можно уже сегодня в приложении GitHub Copilot.</p><p><b>Об авторе.</b> Cassidy Williams — старший директор по адвокации разработчиков в GitHub. Она создаёт ПО, консультирует стартапы и учит разработчиков строить лучше. Подписаться на её еженедельную рассылку можно на <a href="https://cassidoo.co/newsletter">cassidoo.co/newsletter</a>.</p><h2>Выводы</h2><p>Git worktrees — способ работать с несколькими ветками одновременно, не прерывая текущую задачу и не рискуя запутаться в stash. Они особенно удобны при параллельной работе с ИИ-агентами и встроены в приложение GitHub Copilot по умолчанию. Попробуйте — возможно, именно они упростят ваш рабочий процесс.</p>]]></content:encoded>
    </item>
    <item>
      <title>EchoBird — настольный менеджер ИИ-инструментов на Rust</title>
      <link>https://tproger.ru/articles/echobird-desktopnyj-menedzher-ii-instrumentov-na-rust</link>
      <comments>https://tproger.ru/articles/echobird-desktopnyj-menedzher-ii-instrumentov-na-rust?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/echobird-desktopnyj-menedzher-ii-instrumentov-na-rust</guid>
      <description><![CDATA[<p>EchoBird — бесплатный настольный менеджер для установки ИИ-агентов, локальных LLM и управления моделями. Узнайте, кому он пригодится и как устроен изнутри.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/echobird-desktopnyj-menedzher-ii-instrumentov-na-rust">EchoBird — настольный менеджер ИИ-инструментов на Rust</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Нейронные сети]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 16 Jun 2026 08:30:33 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://echobird.ai/">EchoBird</a> — open-source настольный менеджер для установки ИИ-агентов и локальных больших языковых моделей (LLM). Если вы хоть раз объясняли другу, как установить Claude Code, настраивали API-ключи для Codex CLI или искали, куда пропал config.json после очередного эксперимента с локальной моделью, вы знаете главную боль запуска ИИ-инструментов на новой машине: каждый инструмент живёт по своим правилам. EchoBird сводит эту кашу в одно кроссплатформенное приложение.</p><p>EchoBird — это open-source настольный лаунчер для ИИ-агентов и локальных больших языковых моделей (LLM). Написан на Rust + Tauri, работает на Windows, macOS и Linux. Автор, разработчик с ником edison7009, собрал его после того, как друзья не раз просили помочь «поставить этот ваш ИИ» на новой машине. Сейчас проект насчитывает 2200 звёзд на GitHub, 162 ответвления и 112 релизов; актуальная версия — v5.2.7.</p><p>Идея простая: вместо того чтобы запускать терминал для каждого инструмента, вбивать команды установки и копаться в переменных окружения, вы открываете одно окно. Там устанавливаются агенты, подключаются API-ключи, запускаются локальные модели и управляются собственные ИИ-проекты. В российских условиях локальные LLM и альтернативные поставщики часто воспринимаются не как эксперимент, а как рабочая необходимость: ограничения на доступ к западным сервисам и требования к данным делают собственное железо привлекательнее облака.</p><ul><li>EchoBird — это open-source менеджер ИИ-инструментов на Rust + Tauri для Windows, macOS и Linux.</li><li>Четыре сценария: установка и починка агентов, локальные LLM, личные ИИ-проекты и единое средство запуска приложений.</li><li>Model Nexus объединяет API-ключи, модели и протоколы OpenAI / Anthropic в одном центре конфигурации.</li><li>Локальные модели запускаются через llama.cpp, vLLM или SGLang с автоподбором под железо.</li><li>Расширяется через plugin.json: новый инструмент можно добавить без изменения кода приложения.</li></ul><h2>Четыре сценария, которые покрывает EchoBird</h2><p>Приложение не претендует на роль универсального рабочего стола, а решает конкретные задачи вокруг «поставил — настроил — запустил». Все четыре сценария используют общий Model Nexus — о нём ниже.</p><h3>Установка и починка агентов</h3><p>EchoBird сканирует систему, находит уже установленные инструменты и проверяет зависимости. Если чего-то не хватает, ведёт установку в режиме диалога. Если что-то сломалось — запускает починку. Поддерживаются Claude Code, Codex CLI, OpenCode, Aider, Hermes Agent, MiMo Code от Xiaomi и собственная линейка агентов автора — OpenClaw, ZeroClaw, NanoBot и PicoClaw. Новый инструмент добавляется через файл plugin.json, поэтому сообщество может расширять список без запросов на слияние в основной репозиторий.</p><h3>Локальные LLM в один клик</h3><p>Встроены три runtime'а под разное железо: llama.cpp для CPU и слабых машин, vLLM для серверных GPU NVIDIA на Linux и SGLang для агентских сценариев с жёстко заданным выводом — например, когда модель должна вернуть JSON строго по схеме. EchoBird сам определяет наличие видеокарты NVIDIA и предлагает runtime с подходящими параметрами — например, размер батча и offloading слоёв на GPU. После запуска модель отдаёт совместимые с OpenAI и Anthropic endpoints — остальные инструменты не замечают подмены. Про локальный запуск LLM через Ollama у нас есть <a href="https://tproger.ru/articles/zapuskaem-llm-lokalno-cherez-ollama-gajd-ot-ustanovki-do-claude">пошаговый гайд</a>.</p><h3>Мои ИИ-проекты</h3><p>Раздел для утилит, демок, игр и скриптов, созданных в режиме vibe coding (импровизационной разработки с ИИ), которые рождаются за один вечер с ИИ-помощником. Вместо того чтобы искать директорию и вспоминать команду запуска, проект живёт в едином списке с кнопкой «старт».</p><h3>Менеджер приложений</h3><p>Центральная панель запуска для всего, что уже установлено. Можно запустить Claude Code, Codex CLI или локальную модель из одного списка, не открывая отдельные терминалы и окна.</p><h2>Model Nexus: единая точка входа для моделей</h2><p>Главная архитектурная идея — Model Nexus. Это прослойка между приложением и поставщиками моделей, которая хранит API-ключи, проверяет задержки, транслирует OpenAI- и Anthropic-совместимые протоколы.</p><p>Без него каждый инструмент требует своей конфигурации: Claude Code требует свой API-ключ, Codex CLI — свой .env, Hermes Agent — свой config.toml, а локальный vLLM — свой путь к модели. Model Nexus заменяет это на «ввёл один раз, используешь везде».</p><p>Поддерживаются 14+ поставщиков: Anthropic, OpenAI, Gemini, xAI Grok, Mistral, Together AI, DeepSeek, MiniMax, GLM, SiliconFlow, а также Ollama, llama.cpp, vLLM, SGLang, OpenRouter и любой OpenAI-совместимый endpoint. Каждому агенту можно назначить свой протокол независимо от остальных.</p><h2>Почему Tauri + Rust, а не Electron</h2><p>Выбор стека для менеджера, который управляет чужими тяжёлыми моделями, логичен. Tauri даёт бинарник менее 10 МБ против типичных 100+ МБ у Electron. Rust напрямую дёргает системные API, работает с файловой системой, процессами и детекцией GPU без лишних мостов. Для инструмента, который постоянно запускает подпроцессы и читает конфигурации, это надёжнее и предсказуемее.</p><h2>Сравнение: классическая установка Claude Code и EchoBird</h2><p>Традиционный путь — пять шагов, каждый из которых может обломаться:</p><ol><li>Проверить версию Node.js (нужен 18+).</li><li>Убедиться, что права npm настроены правильно.</li><li>Выполнить npm install -g @anthropic-ai/claude-code.</li><li>Прописать переменную окружения ANTHROPIC_API_KEY.</li><li>Проверить: claude --version. При смене машины — повторить.</li></ol><p>Путь через EchoBird — короче в разы:</p><ol><li>Открыть EchoBird.</li><li>Install &amp; Repair → Claude Code → установить в один клик.</li><li>Model Nexus → ввести API-ключ один раз для всех инструментов.</li></ol><h2>Расширение через plugin.json</h2><p>Новый инструмент описывается JSON-файлом с командами установки, проверки, починки и запуска. Пример минимального плагина:</p><p>Такой подход отделяет ядро приложения от рецептов установки. Теоретически сообщество может добавить плагины для российских моделей — например, GigaChat, YandexGPT или локальных сборок на базе Saiga — без необходимости ждать официальных обновлений.</p><h2>Что нового в ветке v5.x</h2><p>Релизы выходят активно. Несколько примечательных изменений последних недель:</p><ul><li>v5.2.4 — зеркала для установки из Китая (Tsinghua, Alibaba, Huawei), чтобы зависимости качались без VPN.</li><li>v5.2.0 — переключатель Responses-протокола OpenAI для моделей, которые говорят на нём нативно, например MiniMax-M3 и Qwen3.7.</li><li>v5.2.6 — поддержка MiMo Code от Xiaomi и починка автоуплотнения контекста в Codex V2, из-за которого разговоры обрывались.</li><li>v5.2.4 — нативные элементы управления окном и стандартные горячие клавиши macOS (⌘W, ⌘M, ⌘,).</li></ul><h2>Как попробовать</h2><p><b>Безопасность:</b><br />Перед запуском любого установочного скрипта из интерната рекомендуем открыть его в редакторе или скачать официальный инсталлятор из <a href="https://github.com/edison7009/EchoBird/releases">GitHub Releases</a>.</p><p>После установки: открываете Model Nexus, добавляете ключи; переходите в Install &amp; Repair, выбираете инструменты; запускаете их из App Manager. Для локальных моделей — Local LLM → runtime → модель.</p><h2>Кому пригодится</h2><ul><li>Тимлидам и DevRel'ам, которым часто нужно «поднять окружение» на новой машине коллеги.</li><li>Разработчикам, кто переключается между рабочими и личными ПК и устал от повторяющегося адаптации.</li><li>Энтузиастам локальных LLM, которым надоело вручную ставить CUDA, vLLM и искать пути к моделям.</li><li>Авторам плагинов, которые хотят добавить поддержку отечественных или нишевых ИИ-сервисов.</li></ul><h2>Выводы</h2><p>EchoBird не изобретает ИИ-инструменты заново — он убирает трение на границе между «хочу попробовать» и «уже работает». Model Nexus решает проблему разрозненных конфигураций, встроенные runtime'ы — проблему локальных моделей, а плагины — проблему поддержки новых сервисов. Выбор Rust + Tauri выглядит обоснованным: приложение остаётся лёгким, хотя самые тяжёлые модели грузятся вне его.</p><blockquote>EchoBird родился из собственной боли: друзья постоянно просили меня установить Claude Code на их машины. Я хотел, чтобы любой мог нажать одну кнопку и получить рабочее окружение, не разбираясь в Node.js, npm и переменных окружения.</blockquote><p>Если в вашем рабочем процессе регулярно фигурируют Claude Code, Codex CLI, Aider или локальные LLM, стоит потратить несколько минут на установку и посмотреть, сколько ручных шагов отпадёт.</p><h2>Источники</h2><p>Оригинальный материал: <a href="https://dev.to/wonderlab/open-source-project-of-the-day-97-echobird-one-app-to-install-configure-and-run-all-your-ai-2bpi">Open Source Project of the Day (#97): EchoBird</a> — DEV Community. Дополнительно: <a href="https://github.com/edison7009/EchoBird">репозиторий EchoBird на GitHub</a>, <a href="https://echobird.ai/">официальный сайт</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как правильно использовать поля HTML-форм: гайд для разработчиков</title>
      <link>https://tproger.ru/articles/kak-pravilno-ispolzovat-polya-html-form-polnyj-gajd</link>
      <comments>https://tproger.ru/articles/kak-pravilno-ispolzovat-polya-html-form-polnyj-gajd?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-pravilno-ispolzovat-polya-html-form-polnyj-gajd</guid>
      <description><![CDATA[<p>Разбираем типы полей HTML-форм, правила доступности, ARIA-атрибуты и лучшие практики. Узнайте, как создавать удобные и конверсионные формы для любых устройств.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-pravilno-ispolzovat-polya-html-form-polnyj-gajd">Как правильно использовать поля HTML-форм: гайд для разработчиков</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Фронтенд-разработка с нуля]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 04 Jun 2026 09:55:25 GMT</pubDate>
      <content:encoded><![CDATA[<p>Неправильно свёрстанная форма — серьёзный источник раздражения пользователей на сайте. Поле email без подходящей клавиатуры на телефоне, чекбокс без label, который невозможно попасть пальцем, или форма, которая отправляется при случайном нажатии Enter — всё это ежедневно встречается даже на крупных проектах. Разбираем, как избежать этих ошибок и сделать формы удобными для всех пользователей.</p><h2>Что такое поля форм</h2><p>Поля форм — это интерактивные элементы HTML, через которые пользователь взаимодействует с веб-страницей. Браузер отображает их по-разному в зависимости от типа: для email показывает клавиатуру с символом @, для даты — календарь, для файла — диалог выбора документа.</p><p>Главное правило: под каждую задачу существует своё поле. Использовать универсальный &lt;input type="text"&gt; везде — значит лишать пользователя встроенных удобств браузера.</p><p>{'id': 'a94adbf9d2', 'data': {'text': '<b>Правильный тип input</b> — залог удобства: браузер сам подставит нужную клавиатуру и проверит формат.'}, 'type': 'paragraph'}</p><p>{'id': 'd3748dda0e', 'data': {'text': '<b>Label обязателен</b> для каждого поля: связывайте его через атрибут for с id поля.'}, 'type': 'paragraph'}</p><p>{'id': 'c924682827', 'data': {'text': '<b>Группируйте связанные поля</b> в &lt;fieldset&gt; с &lt;legend&gt; — это помогает средствам чтения с экрана и структурирует форму.'}, 'type': 'paragraph'}</p><p>{'id': '49afc5a27c', 'data': {'text': '<b>Не забывайте name</b>: без него данные не попадут в запрос при отправке формы.'}, 'type': 'paragraph'}</p><p>{'id': 'ab33c8052b', 'data': {'text': '<b>Используйте &lt;button type="submit"&gt;</b> вместо устаревшего &lt;input type="submit"&gt; — это гибче и семантичнее.'}, 'type': 'paragraph'}</p><h2>Основные элементы форм</h2><h3>input — универсальный инструмент ввода</h3><p>Элемент &lt;input&gt; — самый распространённый в формах. Его внешний вид и поведение полностью зависят от атрибута type. Браузер поддерживает свыше 20 типов: от классического text до специализированных date, tel, range и даже color.</p><p>Когда вы выбираете конкретный тип, браузер берёт на себя три задачи: отображает подходящий интерфейс, показывает оптимальную экранную клавиатуру на мобильных устройствах и применяет встроенную проверку. Например, type="email" проверит наличие символа @ до отправки на сервер.</p><p>Вот типы input, которые стоит знать каждому фронтенд-разработчику:</p><ul><li>text — текстовая строка, базовый тип.</li><li>email — проверяет наличие @ и домена, показывает email-клавиатуру.</li><li>tel — цифровая клавиатура на мобильных устройствах.</li><li>number — ограничивает ввод числами, добавляет стрелки.</li><li>date — нативный календарь браузера.</li><li>checkbox и radio — выбор одного или нескольких вариантов.</li><li>file — загрузка файлов с нативным диалогом выбора.</li><li>range — ползунок для выбора значения из диапазона.</li><li>color — нативный выбор цвета.</li><li>search — поле поиска с кнопкой очистки.</li></ul><p>Атрибут required делает поле обязательным для заполнения, а pattern позволяет задать регулярное выражение для проверки введённых данных без JavaScript.</p><h3>label — связываем текст с полем</h3><p>Без подписи поле ввода теряет смысл. Элемент &lt;label&gt; решает эту задачу и одновременно делает форму доступной для пользователей с ограничениями зрения.</p><p>Есть два способа связать label с полем. Первый — явный: атрибут for на label должен совпадать с id на input. Второй — вложенный: input размещается внутри label. Оба варианта работают, но явная связь через for гибче: позволяет располагать label и input в разных частях разметки.</p><p><b>Важно:</b><br />Клик по label автоматически переводит фокус в связанное поле. Это удобно на десктопе и критично на мобильных устройствах, где площадь касания имеет значение.</p><h3>textarea — когда одной строки мало</h3><p>Для длинных текстов — комментариев, отзывов, описаний — используйте &lt;textarea&gt;. В отличие от &lt;input&gt;, он поддерживает переносы строк и растягивается по содержимому. Атрибуты rows и cols задают начальные размеры, а CSS — финальное оформление.</p><h3>select и datalist — выбор из вариантов</h3><p>Когда нужно предложить пользователю готовый список вариантов, на помощь приходит &lt;select&gt;. Он отображает выпадающий список, в котором можно выбрать один или несколько пунктов. По умолчанию выбран первый элемент, но через атрибут selected можно задать другой.</p><p>Альтернатива — &lt;datalist&gt;. Он работает как автодополнение: пользователь может как выбрать вариант из списка, так и ввести свой собственный текст. Это удобно для полей вроде «профессия» или «название компании», где невозможно предусмотреть все варианты.</p><h3>fieldset и legend — группируем поля</h3><p>Формы с десятком полей выглядят как стена текста. Чтобы структурировать их, используйте &lt;fieldset&gt; с обязательным дочерним элементом &lt;legend&gt;. Такая группировка помогает пользователям ориентироваться и критична для средств чтения с экрана: они объявляют legend перед каждым полем в группе.</p><h3>ARIA-атрибуты для сложных форм</h3><p>Для скринридеров и пользователей с ограничениями зрения важно не только правильное использование label, но и дополнительные ARIA-атрибуты. Например, aria-required="true" сообщает о необходимости заполнения, а aria-describedby связывает поле с текстом подсказки или сообщения об ошибке.</p><p>Атрибут aria-live="polite" на контейнере с ошибками позволяет скринридеру озвучить сообщение без прерывания текущего действия пользователя. Это особенно важно для динамических форм, где ошибки появляются после асинхронной проверки на сервере.</p><h3>Как отправить форму</h3><p>Для отправки данных используйте &lt;button type="submit"&gt;. Это семантично, стилизуется гибче, чем &lt;input type="submit"&gt;, и поддерживает вложенный HTML-контент — например, иконку рядом с текстом.</p><p><b>Важно:</b><br />Если у кнопки не указан атрибут type, браузер считает её кнопкой отправки по умолчанию. Чтобы избежать случайной отправки, всегда явно указывайте type="button" для кнопок, которые не должны отправлять форму.</p><p>Помимо клика по кнопке, форма отправляется при нажатии клавиши Enter в однострочных полях ввода. В многострочном &lt;textarea&gt; Enter добавляет перенос строки. Это стандартное поведение браузера, которое стоит учитывать при проектировании многошаговых форм: случайная отправка на середине заполнения раздражает пользователей.</p><h2>Чек-лист: правильная форма за 5 минут</h2><ul><li>Каждое поле имеет связанный &lt;label&gt; через for и id.</li><li>Выбран подходящий type для &lt;input&gt; — не везде text.</li><li>У всех полей есть атрибут name, иначе данные не уйдут на сервер.</li><li>Связанные поля объединены в &lt;fieldset&gt; с &lt;legend&gt;.</li><li>Для длинных текстов используется &lt;textarea&gt;, а не многострочный input.</li><li>Кнопка отправки — &lt;button type="submit"&gt;, а не устаревший input.</li><li>Форма работает без JavaScript: базовая проверка и отправка происходят на уровне HTML.</li></ul><h2>Выводы</h2><p>Поля HTML-форм — это не просто визуальные элементы, а полноценный интерфейс взаимодействия пользователя с вашим приложением. Правильный выбор типов input, связь через label, группировка в fieldset, ARIA-атрибуты и семантичная кнопка отправки — всё это влияет на удобство, доступность и конверсию.</p><blockquote>Доступность не добавляется в проект в конце — она закладывается на этапе разметки. Правильно свёрстанная форма работает для всех пользователей без дополнительных усилий.</blockquote><p>Если хотите углубиться в тему — изучите <a href="https://web.dev/learn/forms/">полный курс по формам от Google</a>. Там разбираются проверка данных, оформление, доступность и даже usability-тестирование поведения пользователей при заполнении форм.</p><p>Источник: <a href="https://web.dev/learn/forms/form-fields/">web.dev — Help users enter data in forms</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Где найти хакатон в России? Подборка самых проверенных площадок</title>
      <link>https://tproger.ru/articles/gde-najti-hakaton-v-rossii-podborka-samyh-proverennyh-ploshhadok</link>
      <comments>https://tproger.ru/articles/gde-najti-hakaton-v-rossii-podborka-samyh-proverennyh-ploshhadok?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Валерия Турчак]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/gde-najti-hakaton-v-rossii-podborka-samyh-proverennyh-ploshhadok</guid>
      <description><![CDATA[<p>Подборка проверенных площадок и агрегаторов для поиска хакатонов, CTF, ML-челленджей и IT-митапов в России. Разбираем форматы, призовые фонды и пользу для разработчиков, PM и QA. Забирайте календарь соревнований и получайте офферы!
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/gde-najti-hakaton-v-rossii-podborka-samyh-proverennyh-ploshhadok">Где найти хакатон в России? Подборка самых проверенных площадок</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Конференции]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Соревнования]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 29 May 2026 04:41:50 GMT</pubDate>
      <content:encoded><![CDATA[<p>Искать годный хакатон сегодня — занятие для тех, кто знает, что хочет найти. Сегодня в поиске можно найти лендинги с просроченными дедлайнами, местечковые конкурсы за грамоту и агрегаторы, которые последний раз обновлялись года два назад. При этом разработчикам, PM, QA и дата-саентистам нужны нормальные ивенты. Хочется за выходные обкатать пет-проект, забрать призовые, поработать с незнакомым стеком или просто переключиться с рутинных тасок.</p><p>Мы собрали площадки, которые регулярно публикуют актуальные анонсы российских ИТ-соревнований. В подборке разобрали платформы, а  вы выбирайте подходящий и идите собирать команду.</p><h2>1. Хакатоны.рус — агрегатор ИТ-соревнований и организатор кастомных турниров</h2><p>Проект Хакатоны.рус работает в двух направлениях. Во-первых,<a href="https://www.xn--80aa3anexr8c.xn--p1acf/"> это портал</a>, где собирают анонсы турниров по России и СНГ для разработчиков с любым опытом — от школьников до сеньоров. Жесткой привязки к стеку у ресурса нет, главное требование к анонсам — наличие соревновательной механики.</p><p>Во-вторых, команда выступает самостоятельным организатором. Проект входит в экосистему <a href="https://xn--80auseji.xn--p1acf/">«Хакрус групп»</a> и через агентство по организации технологических мероприятий «Хакрус» проводит соревнования для коммерческого сектора на базе собственной платформы. На текущий момент командой реализовано более 200 соревнований разного масштаба и формата.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/f4637058-a130-45a9-a92a-451b2db43a3d.webp" alt="" /><figcaption>Smolathon 2025 в рамках форма “Свой код”</figcaption></figure><p>Кроме коммерческих проектов, организаторы делают и свои собственные. Внутренние турниры ориентированы на джунов с опытом, мидлов и студентов старших курсов. Команда делает упор на продуктовое программирование, ML-задачи и формат Capture the Flag. Форматы распределяются равномерно: можно писать код дистанционно, приехать на классический офлайн-забег или участвовать в гибридном режиме. В эту же экосистему входят отдельные проекты и лендинги, например <a href="https://fintechacademy.ru/">«Академия Финтеха»</a> и турнир <a href="https://www.xn--80aa3anexr8c.xn--p1acf/letshack">«Лет’с хак»</a>.</p><h2>Нетворкинг и польза для разработчиков</h2><p>Помимо календаря с фильтрами, портал ежемесячно выпускает дайджест будущих событий и развивает профильный блог. В статьях разработчикам объясняют базу. Там можно почитать, <a href="https://www.xn--80aa3anexr8c.xn--p1acf/blog/tpost/7edb13uju1-hakaton-eto">что такое хакатон</a> в принципе, найти <a href="https://www.xn--80aa3anexr8c.xn--p1acf/blog/tpost/ksmt195lp1-soveti-po-podgotovke-k-rabote-s-eksperta">советы по подготовке</a> и работе с экспертами, а также изучить <a href="https://www.xn--80aa3anexr8c.xn--p1acf/blog/tpost/2d7ci16je1-startapi-sozdannie-na-hakatonah">стартапы, созданные на хакатонах</a>.</p><p>Для финалистов соревнований собрали закрытое комьюнити — Community of Hackathon Winners. Туда приглашают победителей и представителей компаний-заказчиков. Участники получают прямой доступ к лидам: можно обсудить архитектурные решения без посредников или забрать оффер без классической эйчар-воронки.</p><h2>Разбор кейсов: от продуктовой разработки до ИБ</h2><p>Оценить уровень задач проще на примерах недавних ивентов. Осенью 2025 года прошел гибридный Smolathon в рамках форума «Свой код». Заказчиком выступило Минцифры Смоленской области. Команды от 2 до 5 человек создавали аналитический дашборд и имиджевый сайт для регионального ЦОДД. Отборочный онлайн-этап с 22 сентября по 3 октября собрал более 500 участников. В финале 16–17 октября 22 команды доводили продукт до состояния MVP и делили призовой фонд в 500 000 рублей. Подобные продуктовые задачи Хакрус регулярно интегрирует и в масштабные проекты вроде Кубка России по спортивному программированию.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/4926b4cc-d4bd-4fc1-a715-d084debcb073.webp" alt="" /><figcaption>Кубок России по спортивному программированию 2025</figcaption></figure><p>Специфичные технические направления выделяют в узкопрофильные турниры. С 11 по 26 апреля 2026 года состоялся дататон «Криптонит.Тембр» с призовым фондом 600 000 рублей. Специалистам предложили обучить ML-модель распознавания дикторов (Speaker Recognition), которая сохраняет устойчивость к аудиоискажениям в реальных речевых интерфейсах. Ивент собрал свыше 1300 регистраций из 15 городов.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/99e6fdbc-fe46-4ce8-97c3-47df6b3bd43a.webp" alt="" /><figcaption>Дататон «Криптонит.Тембр» 2026</figcaption></figure><p>Иногда соревнования решают исключительно внутренние задачи компаний. С 16 по 23 октября 2025 года для 56 ИТ-специалистов «Комус Технологии» (Комус CTF) провели закрытый Task-Based CTF. Интересно, что участники вообще не занимались информационной безопасностью. Турнир использовали, чтобы на практике познакомить их с проблематикой ИБ. При этом для профильных безопасников команда проводит хардкорные открытые соревнования систем информационной безопасности в рамках фестиваля «Победа».</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/03c6b5fb-2fa8-4821-a7bf-d685dbb95870.webp" alt="" /><figcaption>Соревнования по программированию систем информационной безопасности в рамках фестиваля “Победа”</figcaption></figure><h2>2. Hacklist.ru — календарь ИТ-соревнований с фильтрацией по стеку</h2><p>Проект <a rel="nofollow noopener" href="https://hacklist.ru/">Hacklist.ru</a> собирает базу российских и зарубежных ИТ-ивентов с 2014 года. Сейчас это связка из веб-календаря, Telegram-канала и  соцсети Max. Команда не делает свои турниры, а находят чужие хакатоны, интенсивы и смежные форматы вроде олимпиад по программированию. Фишка ресурса — продуманная система тегов. Вы можете отсеять выдачу по формату (онлайн или офлайн), локациям вроде Москвы и Питера, либо сразу искать нужные технологии: Python, Machine Learning, Deep Learning, компьютерное зрение.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/1896feeb-bbdd-43f5-ab25-0d950db3de2e.webp" alt="" /></figure><h3>Выбор турниров под грейд и карьерный запрос</h3><p>Искать мероприятия удобно под текущую карьерную задачу. Если нужна первая строчка в резюме — кликаете на теги «Для студентов» или «Стажировки» и забираете анонсы образовательных программ.</p><p>Опытным технарям площадка подкидывает соревнования с пометками «Для взрослых» и «Призовой фонд». Глобальный смысл участия — за выходные собрать рабочий прототип, потрогать незнакомые инструменты руками и показать себя лидам. А чтобы не завалить питч, на сайте можно почитать профильный блог. Редакция разбирает стратегии победы, востребованные рынком компетенции, специфику медицинских хакатонов и способы быстро прокачать ML-навыки во время состязаний.</p><h3>Разброс кейсов: от макбука за геймтон до 15 миллионов в DS</h3><p>Сетка анонсов покрывает задачи разного масштаба. Для ML-специалистов весной 2026 года прошел онлайн-турнир MOEX AI HACKATHON с фондом в 1 000 000 рублей и дататон «Криптонит.Тембр» по распознаванию голоса (Speaker Recognition) за 600 000 рублей. В гибридном формате МТС организовал True Tech Hack с пулом в полтора миллиона.</p><p>Если хочется писать код вживую — есть регулярная серия офлайн-хакатонов Tender Hack. Они проходят в Москве, Перми, Казани, Владивостоке и других регионах: там ограниченное число участников пилит умные помощники для тендеров за 500 000 рублей. Если же хардкорный кодинг сейчас не в приоритете, можно залететь на геймтон DatsSol от DatsTeam и попробовать выиграть Apple MacBook Air 13 или Яндекс Станцию Мини.</p><h2>3. Codenrock для IT-соревнований и отборов в бигтех</h2><p>Российская платформа <a rel="nofollow noopener" href="https://codenrock.com/">Codenrock</a> закрывает сразу две потребности: работает как единый календарь мероприятий и предоставляет инфраструктуру для проведения турниров. Здесь не просто собирают анонсы, а физически проводят хакатоны, ML-челленджи, CTF-соревнования и состязания по спортивному (алгоритмическому) программированию. На платформе зарегистрированы тысячи разработчиков, для которых формируется сквозной рейтинг: можно посмотреть лидерборд, заработанные очки и количество пройденных турниров каждого специалиста.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/0ee9f901-e8ef-41da-b48d-ec86bc225441.webp" alt="" /></figure><p>Плюс платформы в том, что она заточена под прямой хантинг. Большие корпорации используют Codenrock для быстрых выходных отборов (Weekend Offer), где можно пропустить долгие собеседования с HR и забрать рабочий контракт за пару дней интенсивного кодинга.</p><h3>От джуна до сеньора: форматы участия</h3><p>Аудитория платформы максимально широкая. Для школьников и студентов младших курсов регулярно запускаются образовательные треки, летние интенсивы и стажировки. Разработчики уровня Junior+ и Middle могут залетать на хардкорные хакатоны с призовым фондом от 1 500 000 рублей.</p><p>Отдельная категория контента — мероприятия для узких специалистов: ML-инженеров, аналитиков и безопасников. Для них на платформе проходят профильные турниры. Компании могут проводить как открытые соревнования для всех желающих, так и закрытые челленджи, доступные только по приватной ссылке.</p><h3>Что внутри: отборы в Яндекс и защита инфобеза</h3><p>На Codenrock регулярно проходят турниры с большими чеками. Весной 2026 года здесь запустили Yandex ML Challenge с главным призом в 1 000 000 рублей и наборами умных устройств. Параллельно Яндекс провел серию фаст-треков: Intern Week Offer Frontend для стажеров-разработчиков и Yandex Weekend Offer для ML-инженеров и аналитиков. Итог таких ивентов — прямой офер в команду после прохождения профильных секций и лайвкодинга.</p><p>Для кибербеза есть соревнования формата CTF. Например, в мае 2026 года стартовал гибридный турнир GO CTF 2026 для школьников и студентов СПО. Призовой фонд в 700 000 рублей команды забирали за решение задач по криптографии, стеганографии и обратной инженерии. А для профессионалов международного уровня на платформе анонсирован SAS CTF 2026 с фондом $18 000 и оплаченным финалом на Бали. Плюс Codenrock организует нестандартные отраслевые форматы — например, недавний ИИ-хакатон для киноиндустрии Wink AI Challenge собрал более 1200 участников, которые писали софт для работы со сценариями.</p><h2>4. Телеграм-агрегатор турниров, митапов и интенсивов для IT-специалистов</h2><p>Проект <a href="https://t.me/iteventsrus?utm_source=chatgpt.com">IT мероприятия России</a> ведут как канал в Telegram. Авторы канала не организуют собственные хакатоны, а работают как агрегатор: собирают по сети анонсы ИТ-событий, конференций, вебинаров и практических интенсивов, чтобы вам не приходилось мониторить десяток разных площадок.</p><p>Они публикуют хакатоны по всей стране — от Москвы и Питера до региональных ИТ-хабов. Выбирайте формат под себя: в ленте есть полностью удаленные задачи, классический офлайн-нетворкинг и гибриды, где вы пишете код на онлайн-отборе, а защищать проект едете на очный финал.</p><p>В канале фокус держится на технарях и продуктовых командах. Анонсы собирают для бэкендеров, фронтендеров, мобильщиков, QA, DevOps и ML/AI-специалистов. Есть также подборки событий под актуальный стек: от Data Science и кибербезопасности до хардкорных инженерных соревнований.</p><h3>Зачем читать ленту и кому это полезно</h3><p>Искать ивенты руками — долго. Канал берет на себя рутину: вы просто скроллите ленту и выбираете, где на выходных собрать прототип, решить реальную бизнес-задачу и получить фидбек от экспертов. Читать анонсы имеет смысл независимо от вашего текущего грейда.</p><p>Студентам площадка подкинет образовательные интенсивы и стартовые хакатоны, чтобы понять, как вообще пишется код в команде. Джунам и мидлам канал поможет найти соревнования, чтобы положить в портфолио что-то серьезнее учебного туду-листа и напрямую познакомиться с нанимающими лидами. Сеньорам агрегатор сэкономит часы на ресерч: здесь мелькают хардкорные турниры, запросы на менторство и возможность собрать сильных инженеров для сложного проекта.</p><p>Заходить на ивенты можно в любом составе. Одиночки найдут анонсы, где команды ищут недостающие руки или разрешают пилить проект в соло. Сработанные тимы смогут выбрать площадку для обкатки продуктовой гипотезы и сборки MVP за счет инфраструктуры организаторов. Заодно по частоте и темам постов удобно следить за рынком — сразу видно, какие задачи сейчас горят в финтехе, аналитике, инфраструктуре, искусственном интеллекте и кибербезопасности.</p><h3>Что конкретно публикуют: разбор кейсов</h3><p>Понять специфику контента проще на примерах конкретных постов. Анонсы заранее раскрывают формат, даты, условия участия и профиль задач, хотя данные по точному количеству участников, призовым фондам и трудоустройству в самих публикациях обычно не указываются.</p><p><a href="https://t.me/iteventsrus/2099">Первый пример</a> — анонс RedShift Meetup-3 и финала соревнований по информационной безопасности Eclipse-3, которые прошли 16 мая в Москве.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/b7420864-7f7d-41ab-913d-bcd10df2f67b.webp" alt="" /></figure><p>Мероприятие объединило формат митапа и турнира Defense CTF, где команды вживую защищали реальную инфраструктуру. Событие поддержали партнеры: Гринатом, Элефус, Бастион и Media Effect. Пост четко ориентирован на студентов, начинающих специалистов и тех, кто погружен в кибербезопасность.</p><p><a href="https://t.me/iteventsrus/2098">Второй пример</a> — гибридное московское AI/ML-событие Moscow AI #6: «Агенты, кодинг и реальные кейсы автоматизации». В программе были темы использования ИИ-агентов в разработке и написания поддерживаемого кода с помощью нейросетей.</p><figure><img src="https://media.tproger.ru/user-uploads/99854/2026-05-29/dd636a63-cc00-48b9-af9d-e0b6209c7d1e.webp" alt="" /></figure><p>Целевая аудитория такого анонса — инженеры, продакты, предприниматели и AI/ML-специалисты, которым интересны профильные дата-соревнования и ИИ-челленджи.</p><p><a href="https://t.me/iteventsrus/2089">Ozon Tech Community QA Meetup</a>. Этот московский гибридный ивент собрал разработчиков и QA-инженеров для обсуждения автоматизации тестирования, генерации автотестов через ИИ, разработки фреймворков на Go и метрик качества.</p><p>Отдельный пласт контента — крупные конференции вроде <a href="https://t.me/iteventsrus/2088">АНА’26</a>. В программе заявлены MLOps, LLM, AI-first-продукты, платформы данных и масштабирование цифровых сервисов.</p><p>Канал регулярно подкидывает информацию для аудитории, которая потенциально готова заходить на продуктовые соревнования и сложные ML-хакатоны.</p><h2>5. Агрегатор профильных IT-конференций и вебинаров</h2><p><a rel="nofollow noopener" href="https://it-event-hub.ru/">IT Event Hub</a> — независимый календарь мероприятий для разработчиков, аналитиков и управленцев, который собирает и структурирует инди-разработчик Виктор Веденин. В отличие от платформ с фокусом на хакатоны, этот агрегатор заточен на образовательные и нетворкинговые форматы. Здесь собирают анонсы митапов, практических вебинаров, крупных IT-конференций и интенсивов.</p><p>В базе можно отфильтровать события по городу и формату — платные или бесплатные, офлайн или удаленка. Подборки охватывают широкий стек: от базовых Frontend и Backend до узких направлений вроде Data Science, DevOps, информационной безопасности и работы с инфраструктурным «железом».</p><h3>Кому и зачем мониторить анонсы</h3><p>Календарь пригодится, когда нужно прокачать софт-скиллы, найти нетривиальное решение архитектурной проблемы или просто послушать, как другие команды справляются с легаси. Инженерам и разработчикам площадка сэкономит время на поиск узкопрофильных технических докладов.</p><p>Управленцам, тимлидам и HR-специалистам агрегатор регулярно подкидывает анонсы конференций об управлении процессами (PeopleSense’26), лидерские программы (CEO FAST TRACK в «Магните») и встречи по Enterprise Agile. А если вы ищете первую работу или возможность сменить стек — посмотрите раздел стажировок. Там периодически появляются оплачиваемые программы для аналитиков и дата-сайентистов, например от ВТБ или буткемпы от Авито.</p><h2>Итоги: как выбрать площадку и не потратить время зря</h2><p>Если вы ни разу не участвовали в хакатонах, но хотите попробовать — не нужно мониторить много разных сайтов. Для начала достаточно определиться с форматом.</p><p>Если нужен плотный соревновательный календарь, где есть и турниры, и доступ к комьюнити победителей — стартуйте с <a href="https://www.xn--80aa3anexr8c.xn--p1acf/" rel="nofollow">Хакатоны.рус</a>. Они сами организуют ИТ-соревнования, понимают внутреннюю кухню и дают прямой выход на нанимающих лидов.</p><p>Если привычнее скроллить ленту в мессенджере, подпишитесь на канал <a href="https://t.me/iteventsrus?utm_source=chatgpt.com" rel="nofollow">«IT мероприятия России» в Telegram</a>. Канал подходит для джунов и студентов, которым нужно собрать команду, и для сеньоров, которые выискивают хардкорные профильные турниры или митапы по кибербезу.</p><p>В сухом остатке: хакатоны — это полноценный HR-инструмент и возможность обкатать на инфраструктуре корпораций архитектурные решения, которые вы бы никогда не потянули в одиночку на домашнем сервере. Выбирайте задачу, собирайте команду и идите писать код.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что такое REST API и почему ваш — вероятно, не REST</title>
      <link>https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest</link>
      <comments>https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest</guid>
      <description><![CDATA[<p>6 ограничений Филдинга и почему большинство JSON API соответствуют лишь 2–3 из них. Проверьте, сколько из них выполняет ваш API — с примерами кода.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-takoe-rest-api-i-pochemu-vaw-veroyatno-ne-rest">Что такое REST API и почему ваш — вероятно, не REST</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Паттерны проектирования]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 14 May 2026 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Большинство разработчиков хотя бы раз строили «REST API». Мало кто читал диссертацию, которая его определяет. Этот разрыв между популярным пониманием и оригинальной спецификацией порождает повторяющиеся архитектурные проблемы и нестабильность API.</p><p>REST API — это веб-сервис, удовлетворяющий шести архитектурным ограничениям, выведенным Роем Филдингом в докторской диссертации «Architectural Styles and the Design of Network-based Software Architectures» (UC Irvine, 2000 год). Начав с «нулевого стиля» — пустого набора без ограничений — Филдинг добавлял каждое из них последовательно, анализируя порождаемые ими свойства распределённых гипермедиа-систем (систем, где переходы между состояниями описаны прямо в ответах сервера). Результат получил название «Передача репрезентативного состояния» (Representational State Transfer).</p><p>Индустрия взяла название и проигнорировала большинство ограничений. «REST» теперь означает «любой API, который отправляет JSON по HTTP». Если вы строите публичный API или API для команд за пределами вашей организации — пропущенные ограничения начинают стоить денег.</p><p>REST — это шесть архитектурных ограничений, сформулированных Роем Филдингом в 2000 году, а не «любой JSON-over-HTTP API».</p><p>Большинство API выполняют лишь 2–3 ограничения из шести: клиент-сервер, stateless и частично слоистую систему.</p><p>Самое игнорируемое ограничение — HATEOAS: сервер передаёт клиенту список доступных действий прямо в ответе.</p><p>HATEOAS решает три дорогостоящие проблемы: пагинацию, версионирование API и обнаруживаемость ресурсов.</p><p>Для небольшой команды с одним потребителем пропуск HATEOAS оправдан. Для публичного API — нет.</p><h2>Шесть ограничений REST API: разбор по порядку</h2><h3>Ограничение 1: Клиент-сервер</h3><p>Клиент и сервер имеют разные зоны ответственности. Клиент отвечает за интерфейс, сервер — за данные и логику. Большинство API справляются с этим по умолчанию. Нарушение появляется, когда сервер начинает диктовать, как клиент должен <i>отображать</i> информацию.</p><p>Например, если API возвращает displayOrder: 3 и buttonColor: "#ff0000" для какого-либо действия — это нарушение. Порядок отображения должен следовать из позиции элементов в ответе. Цвет должен определяться семантическим свойством вроде class: ["danger"], которое каждый клиент интерпретирует самостоятельно.</p><h3>Ограничение 2: Stateless (без состояния)</h3><p>Каждый запрос содержит всю информацию, необходимую серверу для его обработки. Сервер не хранит состояние сессии между вызовами.</p><p>Если вы отправляете GET /path-1 с сессионной cookie, и сервер ищет её в памяти, чтобы получить ваш ID пользователя — это серверное состояние. Stateless-версия включает ID прямо в запрос: JWT или тело POST-запроса переносят его вместе с запросом и могут вернуть в ответе для повторного использования клиентом.</p><h3>Ограничение 3: Кэшируемость</h3><p>Ответы должны быть явно или неявно помечены как кэшируемые или некэшируемые. Клиент или промежуточный узел может повторно использовать закэшированные ответы, не обращаясь к серверу. Филдинг рассматривал кэшируемость как архитектурную задачу первого класса, повышающую эффективность и воспринимаемую производительность за счёт снижения средней задержки.</p><p>Большинство JSON API полностью игнорируют кэширование. Вы отправляете GET /articles/42, а в ответе нет ни Cache-Control, ни ETag, ни Last-Modified. Клиент обращается к серверу каждый раз, даже если статья не менялась неделями.</p><h3>Ограничение 4: Единый интерфейс (Uniform Interface)</h3><p>Это главное ограничение. Филдинг разбил его на четыре подограничения — три описываются ниже, четвёртое (HATEOAS) вынесено в отдельный раздел из-за его значимости.</p><p><b>4.1 URI идентифицируют ресурсы.</b> Единообразие здесь — это сама спецификация URI: схема, authority, путь, запрос, фрагмент. Ограничение ничего не говорит о структуре сегмента пути: /articles/42, /x?id=42 и /a/b/c — всё это валидные URI. «Используйте чистые URL-пути» — популярное соглашение и хороший SEO-инструмент, но не то, что требует Филдинг.</p><p><b>4.2 Управление ресурсами через представления.</b> Вы выполняете GET в /whatever, чтобы получить представление ресурса. Заголовок Content-Type сообщает серверу формат тела запроса. Заголовок Accept сообщает, какие медиатипы (форматы обмена данными) поддерживает клиент для ответа.</p><p><b>4.3 Самоописывающие сообщения.</b> Ответ с Content-Type: application/vnd.collection+json сообщает клиенту, как разбирать тело, без каких-либо предположений.</p><h3>Ограничение 4.4: HATEOAS — то, что пропускают почти все</h3><p>HATEOAS (Hypermedia As The Engine Of Application State) — четвёртое подограничение Uniform Interface. С первыми тремя большинство API справляются. HATEOAS — место, где останавливается почти каждый.</p><p>Разница хорошо видна на примере API для списка чтения. Без HATEOAS вы получаете просто данные, похожие на запись в базе:</p><p>Клиент ничего не знает о том, что он может сделать дальше. Чтобы отметить статью как прочитанную, клиент уже должен знать endpoint: PATCH /articles/42 с {"status": "read"}. Разработчик захардкодил эти знания, прочитав документацию. Сам API их не сообщил.</p><p>С HATEOAS сервер сообщает клиенту о доступных действиях в стандартизированном виде. Вот тот же ответ с использованием <a href="https://github.com/kevinswiber/siren">Siren</a> — одного из стандартизированных медиатипов для гипермедиа-API:</p><p>Клиент не хардкодит URL и HTTP-методы. Массив actions сообщает, что можно сделать. Навигация приходит из links. Если статья уже прочитана — сервер исключает действие mark-as-read из ответа. Кнопка исчезает в UI. Без единого условного выражения в клиентском коде.</p><p>Сервер добавляет новое действие — и каждый клиент подхватывает его при следующем запросе, без деплоя. Это принципиальное отличие: сервер управляет доступными переходами состояния.</p><h3>Ограничение 5: Слоистая система</h3><p>Клиент не может определить, общается ли он с конечным сервером или с промежуточным узлом. Балансировщики нагрузки, CDN и API-шлюзы должны быть прозрачны для вызывающей стороны. Большинство API выполняют это ограничение автоматически. Самый распространённый вид нарушения — когда сообщения об ошибках раскрывают имя хоста бэкенд-сервиса, тем самым нарушая прозрачность слоёв.</p><h3>Ограничение 6: Код по требованию (необязательное)</h3><p>Сервер может передавать исполняемый код клиенту. Думайте о JavaScript, подключаемом через тег &lt;script&gt; на веб-странице. Для API это ограничение почти не применимо. Филдинг сделал его единственным необязательным.</p><h2>Что HATEOAS решает на практике</h2><p>Филдинг разработал HATEOAS для решения тех самых проблем, с которыми API-команды сейчас борются вручную, многократно и каждый раз по-разному.</p><h3>Пагинация</h3><p>Без HATEOAS каждый API изобретает собственную схему. Один использует page и pageSize. Другой — offset и limit. Третий — токены на основе курсора. С HATEOAS сервер включает ссылку с отношением «следующая страница». Клиент следует этому отношению. Схема пагинации может измениться, не сломав ни одного клиента: клиент не знал деталей схемы и не знает формата URL.</p><h3>Версионирование</h3><p>Без HATEOAS команды версионируют API через URL-пути (/v1/, /v2/) или кастомные заголовки вроде X-API-Version. Поддержка нескольких версий занимает месяцы, клиенты привязываются к версии и ломаются при её выводе из эксплуатации. С HATEOAS сервер вводит новые действия, добавляя ссылки. Старые ссылки продолжают работать.</p><h3>Обнаруживаемость</h3><p>Без HATEOAS первый шаг разработчика — чтение Swagger-документации, второй — хардкодинг каждого endpoint в клиент. С HATEOAS корень API возвращает ссылки на все доступные ресурсы. Клиент исследует API так же, как браузер исследует веб-сайт.</p><h2>Сколько ограничений выполняет ваш API</h2><p>Итого шесть ограничений. Большинство API удовлетворяют двум-трём: клиент-сервер, слоистую систему и частичную безгосударственность. Большинство нарушают кэшируемость по умолчанию. Большинство полностью игнорируют HATEOAS.</p><ul><li><b>Клиент-сервер</b> — разделение ответственности за интерфейс и данные</li><li><b>Stateless</b> — каждый запрос самодостаточен, без серверных сессий</li><li><b>Кэшируемость</b> — явные заголовки Cache-Control, ETag, Last-Modified</li><li><b>Единый интерфейс</b> — URI, представления, самоописывающие сообщения и HATEOAS</li><li><b>Слоистая система</b> — прозрачность промежуточных узлов</li><li><b>Код по требованию</b> — опционально, для API почти не применимо</li></ul><p>Это не оценка. Это карта компромиссов, которые вы приняли — намеренно или случайно. Для небольшой команды с одним потребителем и Slack-каналом для координации пропуск HATEOAS оправдан. Публичный API с сотнями потребителей платит за каждый пропущенный раунд миграции версий. Подробнее об эволюции REST-архитектуры — в <a href="https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">главе 5 диссертации Филдинга</a>.</p><h2>Выводы</h2><blockquote>Я разочарован тем, что многие не знакомы с 15-летними исследованиями в области гипермедиа, которые стоят за REST. Большинство так называемых REST API — это просто удалённые вызовы процедур через HTTP.</blockquote><p>Пройдитесь по шести ограничениям и посчитайте, сколько из них выполняет ваш API. Это даст не оценку, а карту принятых компромиссов — осознанных или случайных. Упражнение отвечает на один вопрос: ваш API — это Representational State Transfer или просто HTTP-транспорт, закрытый для расширения?</p><p>Оригинальная статья: <a href="https://fagnerbrack.com/what-is-a-rest-api-and-why-yours-probably-isnt-one-7e5fb65ece4d">Fagner Brack — What Is a REST API, and Why Yours Probably Isn't One</a>. Первоисточник: <a href="https://ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm">Глава 5 диссертации Роя Филдинга, UC Irvine</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как JetBrains избавляются от фризов в IntelliJ-IDE: перевод статьи про background write-действия</title>
      <link>https://tproger.ru/translations/kak-jetbrains-izbavlyayutsya-ot-frizov-v-intellij-ide-perevod-stat</link>
      <comments>https://tproger.ru/translations/kak-jetbrains-izbavlyayutsya-ot-frizov-v-intellij-ide-perevod-stat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Картофельный Повелитель]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/kak-jetbrains-izbavlyayutsya-ot-frizov-v-intellij-ide-perevod-stat</guid>
      <description><![CDATA[<p>Перевод статьи JetBrains Platform: как за 7 лет перенесли write-действия с UI-потока в фон. В IntelliJ 2025.3 доля UI-блокировок упала в 3,5 раза.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/kak-jetbrains-izbavlyayutsya-ot-frizov-v-intellij-ide-perevod-stat">Как JetBrains избавляются от фризов в IntelliJ-IDE: перевод статьи про background write-действия</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 18 Apr 2026 11:45:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если у вас при правке большого проекта в IntelliJ IDEA, WebStorm или PyCharm IDE периодически замирает — шестерёнка приколочена к UI-потоку, и за ней стоит очередь из операций, которые нельзя распараллелить. JetBrains вычищают её уже семь лет, и только что отчитались об очередном шаге: доля времени UI-потока, занятая write-действиями, упала с 1,83% до 0,53% между версиями 2025.2 и 2025.3 — в три с половиной раза.</p><p>Это перевод <a href="https://blog.jetbrains.com/platform/2026/04/road-to-responsive-ides/">оригинальной статьи</a> от Patrick Scheibe (по мотивам внутренней статьи Konstantin Nisht) на блоге JetBrains Platform про многолетнюю работу команды по перекладыванию write-операций в фон. Для разработчиков, которые пишут плагины под IntelliJ Platform или просто борются с фризами в IDE — это редкая возможность заглянуть под капот.</p><ul><li>Корень фризов IntelliJ-IDE — single read-write lock на общие структуры данных (PSI, Document, VFS) и single UI-поток AWT (EDT)</li><li>С 2019 года JetBrains переносят write-действия в фон, с паузой в 2020-2022 годах</li><li>В 2024 году появился новый cancellable lock по результатам совместной работы с JetBrains Research</li><li>В 2025 году Konstantin Nisht решил проблему modality (модальные диалоги) — первые write-действия ушли в фон</li><li>Мигрировали: Workspace Model, VFS refresh, document commit</li><li>2025.2 → 2025.3: доля времени UI на write-lock упала с 1,8276% до 0,5298%, в 1% худших случаев — с 5% до 3%</li></ul><p><b>TL;DR</b> от авторов: это технический пост про многолетнюю работу над отзывчивостью IntelliJ-based IDE. JetBrains строят инструменты и API, которые позволяют выполнять нагрузочные операции вне UI-потока. UI-поток теперь держит write-блокировку примерно в три раза меньше времени, чем раньше. Если технические детали не интересуют — листайте к графикам в конце.</p><p>Одна из самых частых жалоб на IntelliJ-based IDE — производительность. Мы знаем. И работаем над тем, чтобы сделать IDE более отзывчивой. Это не всегда просто: платформе IntelliJ 25 лет, и некоторые архитектурные решения впаяны в неё намертво. Именно они делают ряд оптимизаций сложными.</p><h2>Смертоносное переплетение</h2><p>IntelliJ Platform — многопоточный фреймворк, построенный вокруг единой <a href="https://plugins.jetbrains.com/docs/intellij/threading-model.html">read-write-блокировки (RW lock)</a>. IDE работает с несколькими ключевыми структурами данных: <a href="https://plugins.jetbrains.com/docs/intellij/psi.html">синтаксическими деревьями (PSI)</a>, текстовым представлением файлов (Document subsystem) и представлением файловой системы ОС (Virtual File System, VFS). Доступ к этим структурам защищён RW-блокировкой. Операции делятся на read actions и write actions. В каждый момент времени может существовать только одно write-действие; read-действий может быть параллельно сколько угодно, но read- и write-действие одновременно идти не могут.</p><p>Ещё наши IDE — это UI-приложения. Значит, они используют UI-фреймворк. В IntelliJ Platform это Java AWT с единственным UI-потоком: Event Dispatch Thread (EDT). Этот поток обрабатывает пользовательский ввод и отрисовывает интерфейс. Java также позволяет запускать там бизнес-логику. Производительность EDT напрямую определяет ощущение отзывчивости приложения: если поток быстро обрабатывает события перерисовки и ввод — IDE кажется шустрой.</p><p>Отсюда и берутся фризы. Само write-действие может их вызывать: некоторые write-действия, например пересбор синтаксического дерева или обновление представления файловой системы, тяжёлые сами по себе. Другой, менее очевидный источник фризов — ожидание write-блокировки. Поскольку read- и write-действия не могут идти параллельно, запуск write-действия означает ожидание завершения всех активных read-действий. Мы много работали над тем, чтобы read-действия можно было отменять, но проблема не уходит полностью: если хоть одно read-действие неотменяемо — страдает вся IDE.</p><p>Это и привело нас к ключевой цели: <b>перенести write-действия с UI-потока в фон</b>.</p><h2>С благими намерениями</h2><p>Работа над фоновыми write-действиями началась в 2019 году. Этим занялись Valentin Fondaratov, Andrew Kozlov и Peter Gromov.</p><p>Долгие годы код на EDT имел удобный прямой доступ к моделям IntelliJ Platform. С фоновыми write-действиями эта удобная особенность становится проблемой: UI-код больше не может считать, что доступ к модели всегда безопасен без явной координации. Чтобы сохранить совместимость, нужно было заставить работать большое количество старого UI-кода и при этом сделать все зависимости явными.</p><p>Было и другое осложнение. Код на EDT мог сразу запустить write-действие. Для обычных явных read-действий это не так — write-действие не может просто начаться посередине read-действия.</p><p>Здесь в игру вступает write-intent. Это состояние блокировки, которое всё ещё допускает параллельные read-действия, но может быть взято только одним потоком одновременно и атомарно апгрейдится до полноценного write-действия. Такой режим хорошо подходит EDT-коду, которому может потребоваться перейти к write-действию. Его внедрение в платформу стало важным шагом к поддержке фоновых write-операций без ломки текущего поведения.</p><p>В 2020 году проект поставили на паузу — объём требуемых изменений оказался колоссальным. Много UI-компонентов, особенно редактор, опирались на давние допущения о доступе к моделям с EDT.</p><h2>Большой рефакторинг</h2><p>Проект не бросили, а в 2022 году работу возобновили Lev Serebryakov и Daniil Ovchinnikov.</p><p>На этом этапе IntelliJ Platform отрефакторили так, чтобы вывести наружу множество неявных допущений, на которых она держалась. Это уменьшило зависимость части UI-кода от неявной блокировки.</p><p>Другой важной частью стала совместная работа с командой JetBrains Research. Прежняя реализация блокировки предполагала, что write-действия выполняются только на EDT. Перенос их в фон требовал другого типа блокировки, а обычный ReentrantReadWriteLock не подходил под наши нужды. Результат — новая отменяемая (cancellable) блокировка, которая теперь управляет платформой (см. <a href="https://arxiv.org/abs/2504.11389">статью исследования</a>).</p><p>Этот этап длился до конца 2024 года.</p><h2>Когда одной блокировки недостаточно</h2><p>В начале 2025 года эту часть проекта возглавил Konstantin Nisht. К тому моменту мы почти были готовы запустить первые фоновые write-действия. Оставалась одна крупная проблема — modality (модальность).</p><p>Некоторые UI-элементы IDE должны блокировать пользователю возможность взаимодействовать с чем-то другим. Это модальные диалоги, например диалог Settings. В IntelliJ Platform модальность влияет и на модель: пока виден модальный диалог, не связанные с ним write-действия не должны запускаться. Исторически большую часть этого обеспечивал EDT-планировщик — он просто не запускал UI-работу из немодального контекста, пока модальный диалог открыт.</p><p>Фоновые write-действия в эту модель автоматически не вписывались.</p><p>Если на EDT показан модальный диалог, держащий write-intent, то наивная попытка запустить фоновое write-действие может привести к дедлоку. При этом мы хотим, чтобы вычисления внутри диалога двигались вперёд и их не тормозила работа, не связанная с диалогом.</p><p>Чтобы это решить, мы ввели стратегию блокировок с учётом модальности — она разделяет, что происходит внутри модального диалога, а что снаружи. Это сохраняет гарантии, на которые опираются модальные диалоги, и позволяет фоновым write-действиям выполняться.</p><p>Подход также работает для вложенных модальных вычислений — что важно, потому что настоящие модальные процессы не всегда плоские. С этим на месте мы наконец смогли запустить первые фоновые write-действия.</p><h2>Переносим работу, не ломая плагины</h2><p>Первые write-действия перенести в фон было относительно легко. Они жили в Workspace Model и в основном использовались для инвалидации кешей. После этого наступила очередь чего-то посерьёзнее: VFS refresh.</p><p>VFS refresh — это процесс синхронизации событий изменения файлов от операционной системы с внутренними структурами IDE. Помимо применения этих событий refresh вызывает листенеры — код плагинов, реагирующий на изменения файловой системы. Традиционно VFS refresh выполняется в write-действии, и эти листенеры вызываются там же.</p><p>Возникает проблема совместимости. За много лет большое количество кода листенеров стало предполагать, что выполняется на EDT. Некоторые из них естественным образом лезут в UI. Многие живут в плагинах, которыми мы не управляем — поэтому просто взять и изменить модель выполнения в надежде, что всё продолжит работать, нельзя.</p><p>Задача была не только перенести само write-действие в фон, но и сделать это так, чтобы не сломать длинный хвост существующего плагинного кода.</p><p>Базовая идея проста: держать write-действие в фоне, но при необходимости совместимости возвращать конкретную работу листенеров на EDT. Swing даёт для этого синхронную передачу управления через invokeAndWait(...).</p><p>К сожалению, за этим на первый взгляд простым подходом прячется дедлок. Если фоновое write-действие пытается синхронно передать работу на EDT, а EDT в этот момент сам блокирован ожиданием lock — IDE может замереть.</p><p>Чтобы этого избежать, мы ввели внутренний механизм совместимости, который позволяет определённым UI-событиям продолжать продвигаться во время таких ожиданий. Это дало возможность мигрировать инкрементно: перенести тяжёлую write-работу с EDT, при этом сохранив совместимость для листенеров, которые ещё от него зависят.</p><p>Этот подход оказался одним из важнейших кусков проекта. Он позволил мигрировать листенеры постепенно, сохранить совместимость для внешних плагинов — и при этом получить большую часть выигрыша в производительности, перенося в первую очередь самые медленные участки.</p><p>После VFS refresh мы мигрировали и процесс document commit — тот, который перестраивает PSI из документов. Когда базовая инфраструктура фоновых write-действий была на месте, перенос оказался значительно проще.</p><h2>А давайте потом</h2><p>Фоновые write-действия — не панацея. Они сокращают время, которое EDT проводит за выполнением write-действий, но автоматически не убирают время, которое EDT тратит на ожидание блокировок.</p><p>Даже если write-действия выполняются в фоне, EDT всё ещё может попросить доступ на read или write-intent. Пока идёт write-действие — или пока оно ждёт write-lock — такие запросы способны зафризить UI. Отсюда вторая часть проекта: убрать с EDT как можно больше взятий блокировок.</p><p>Особенно проблемной была область редактора. Редактор отрисовывает контент на основании своих моделей — позиции курсора, фолдингов, текста документа. Но модификация документа защищена RW-lock, а редактору всё ещё нужен доступ к этим данным на EDT. Долгое время read-действия были в редакторе повсюду, включая пути отрисовки. Это серьёзная проблема, потому что отрисовка может происходить в любой момент — и редактор мог требовать read-доступ именно тогда, когда нам больше всего нужно, чтобы UI-поток был свободен.</p><p>Здесь мы пошли на прагматичный компромисс. Мы ослабили часть требований к блокировкам на EDT-путях редактора, но сохранили часть записей документа на EDT ради консистентности. Так отрисовка редактора стала менее зависимой от блокировок — хотя мы пока и не перенесли все модификации документа в фон. Это ещё предстоит.</p><p>Ещё одним источником давления на блокировки в EDT был наш API для асинхронных вычислений. Ради совместимости многие такие вычисления всё ещё были связаны с захватом write-intent, а значит, могли зафризить EDT в непредсказуемые моменты.</p><p>Наблюдение здесь простое: если кто-то планирует работу на выполнение асинхронно в UI-потоке — обычно его не волнует точная микросекунда начала. Значит, не всегда нужно блокировать EDT в ожидании write-intent. Во многих случаях можно просто отложить вычисление до момента, когда этот доступ станет доступным. После ряда изменений в планировке UI проблема стала намного менее актуальной.</p><h2>Результаты, планы и благодарности</h2><p>Фоновые write-действия сложны, потому что касаются фундаментальных контрактов IntelliJ Platform. Мы всё ещё строим API и инструменты, которые помогут плагинам отвязать свою логику от EDT. Работа не закончена, но вот где мы сейчас.</p><p>Как метрику мы отслеживаем, сколько времени EDT тратит на выполнение write-действий. Данные собираются через неделю после каждого релиза.</p><p>Например, в 2025.2 у 1% пользователей write-действия занимали 5% UI-времени. В 2025.3 тот же перцентиль — уже 3%. А ожидаемая общая доля времени UI на write-locks на EDT упала с <b>1,8276% в 2025.2</b> до <b>0,5298% в 2025.3</b> — примерно в три с половиной раза.</p><p>В будущем работа сосредоточится на том, чтобы убрать с EDT больше использования write-intent. Мы хотим убрать блокировки из таких частых взаимодействий, как набор текста. Это сложная цель, потому что она требует переосмысления фундаментальных структур — Actions, PSI, Documents. Сложно, но, как мы думаем, выполнимо.</p><p>Спасибо всем, кого мы ещё не упомянули, кто прямо или косвенно участвовал в проекте: Anna Saklakova, Dmitrii Batkovich, Vladimir Krivosheev, Moncef Slimani, Lev Serebryakov, Nikita Koval и другим. Пост начинался как внутренняя статья Konstantin Nisht и был адаптирован для публичного блога Patrick Scheibe — с меньшим количеством breaking changes, чем обычно.</p><h2>Что это значит для разработчиков плагинов</h2><p>Если вы разрабатываете плагин под IntelliJ Platform — изменения в модели блокировок затрагивают вас напрямую. Три практических вывода из статьи:</p><ul><li><b>Листенеры на VFS refresh и document commit теперь могут выполняться в фоне.</b> Если код листенера предполагал EDT — он будет перенесён через механизм совместимости, но ценой общей производительности. Лучше пройтись по своим листенерам и убрать из них прямую работу с Swing, где это возможно</li><li><b>Долгие некооперативные read-действия — самое больное место.</b> Один плагин с неотменяемым read-действием может тормозить всю IDE. Если пишете код, читающий PSI — поддержите отмену (ProgressManager.checkCanceled) и уведомляйте платформу</li><li><b>Write-intent на EDT больше не безопасно захватывать «на всякий случай».</b> Если вашему коду нужна запись — лучше явно запросить фоновое выполнение, чем держать write-intent на UI-потоке. Конкретные API меняются от релиза к релизу — следите за изменениями в документации IntelliJ Platform</li></ul><p>Для обычных пользователей IDE итог простой: если вы обновитесь с 2025.2 на 2025.3 — фризов должно стать меньше, особенно на крупных Gradle- или Maven-проектах. Конкретный эффект зависит от набора плагинов: те, что ещё не адаптированы, могут работать в старом режиме совместимости и не дать полного выигрыша.</p><h2>Выводы</h2><p>Главный урок статьи не технический, а архитектурный. JetBrains семь лет переделывают фундаментальный контракт платформы, и даже сейчас работа не закончена. Переход с модели «всё на EDT» на «write-действия в фоне» — это не оптимизация, а смена условий, на которых держалась вся плагинная экосистема. И они делают это, не ломая внешние плагины.</p><p>Для разработчиков это один из лучших публичных кейсов, как мигрировать большую систему с глубоко зашитыми допущениями. Для пользователей — маленькое утешение: фризы в IDE с новым релизом станут заметно короче.</p><blockquote>Мы хотим убрать блокировки из таких частых взаимодействий, как набор текста. Это сложная цель, потому что она требует переосмысления фундаментальных структур — Actions, PSI, Documents. Сложно, но мы думаем, что это выполнимо.</blockquote><p>Оригинал статьи — на <a href="https://blog.jetbrains.com/platform/2026/04/road-to-responsive-ides/">блоге JetBrains Platform</a>, авторы: <a href="https://github.com/halirutan">Patrick Scheibe</a> и Konstantin Nisht (внутренняя версия), академическая статья про cancellable lock — на <a href="https://arxiv.org/abs/2504.11389">arXiv</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>GitHub Copilot с 24 апреля обучается на вашем коде по умолчанию — как отключить</title>
      <link>https://tproger.ru/news/github-copilot-s-24-aprelya-obuchaetsya-na-vawem-kode-po-umolchaniyu</link>
      <comments>https://tproger.ru/news/github-copilot-s-24-aprelya-obuchaetsya-na-vawem-kode-po-umolchaniyu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/github-copilot-s-24-aprelya-obuchaetsya-na-vawem-kode-po-umolchaniyu</guid>
      <description><![CDATA[<p>С 24 апреля GitHub Copilot Free, Pro и Pro+ начинает собирать код и промпты для обучения моделей. Как сделать opt-out за минуту — инструкция.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/github-copilot-s-24-aprelya-obuchaetsya-na-vawem-kode-po-umolchaniyu">GitHub Copilot с 24 апреля обучается на вашем коде по умолчанию — как отключить</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 18 Apr 2026 10:30:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вы пользуетесь GitHub Copilot Free, Pro или Pro+ — до 24 апреля нужно зайти в настройки и отключить передачу кода в обучение моделей GitHub. После этой даты opt-out по-прежнему работает, но уже собранные данные из тренировочного датасета обратно не забирают — поэтому откладывать нельзя.</p><p>GitHub <a href="https://github.blog/news-insights/company-news/updates-to-github-copilot-interaction-data-usage-policy/">анонсировал</a> изменение политики 25 марта: 30 дней на раздумья, opt-out через настройки, у Business и Enterprise всё по-старому. Сейчас идут последние дни — до дедлайна меньше недели.</p><ul><li>С 24 апреля 2026 года данные Copilot Free, Pro и Pro+ уходят в обучение моделей по умолчанию</li><li>Opt-out — на <a href="https://github.com/settings/copilot/features">github.com/settings/copilot/features</a>, опция «Allow GitHub to use my data for AI model training»</li><li>Business и Enterprise НЕ затронуты — у них запрет в контракте</li><li>Студенты и преподаватели с бесплатным Copilot Pro защищены отдельной политикой GitHub Education</li><li>Приватные репозитории «в покое» не читаются, но пока вы активно работаете — код из них уходит GitHub</li><li>Данные могут передаваться Microsoft и другим компаниям группы, но не сторонним AI-провайдерам</li></ul><h2>Что именно будет уходить в датасет</h2><p>В анонсе перечислен полный список. Опция включает передачу в обучение моделей:</p><ul><li><b>Выходы Copilot</b> — ваши принятые и отредактированные варианты кода</li><li><b>Входы</b> — сами промпты и фрагменты кода, которые показываются модели</li><li><b>Код вокруг курсора</b> — полный контекст, который Copilot видит при подсказке</li><li><b>Комментарии и документация</b>, которые вы пишете в редакторе</li><li><b>Имена файлов, структура репозитория</b>, паттерны навигации</li><li><b>Все взаимодействия с фичами Copilot</b> — чаты, инлайн-саджесты</li><li><b>Оценки подсказок</b> — лайки и дизлайки по результатам</li></ul><p>Что явно исключено: контент issues, discussions и приватных репозиториев «в покое» (at rest). Но GitHub уточняет отдельно: «Copilot обрабатывает код из приватных репозиториев, пока вы активно используете Copilot. Эти данные нужны для работы сервиса и могут быть использованы для обучения — если вы не сделаете opt-out».</p><p>Фактически это означает: пока вы пишете код в приватном репозитории с включённой опцией, все фрагменты, которые видит Copilot, могут уйти в тренировочный датасет. Приватность в этом режиме ограничивается отсутствием массового сканирования репозитория — но не отсутствием ваших команд в логах.</p><h2>Кого касается, а кого нет</h2><ul><li><b>Затронуты:</b> Copilot Free, Copilot Pro, Copilot Pro+</li><li><b>Исключены:</b> Copilot Business и Copilot Enterprise — запрет в контракте</li><li><b>Исключены:</b> студенты и преподаватели с бесплатным Copilot Pro</li><li><b>Исключены:</b> пользователи, которые ранее отписались от сбора «для улучшения продукта» — preference переносится автоматически</li></ul><p>То есть если у вашей команды корпоративная подписка Business или Enterprise — волноваться не о чем, контракт защищает. А вот у фрилансеров и мелких команд на Pro-тарифе — по умолчанию сбор включён.</p><h2>Как сделать opt-out за минуту</h2><p>Шаги для обычной учётной записи GitHub:</p><ol><li>Открыть <a href="https://github.com/settings/copilot/features">github.com/settings/copilot/features</a></li><li>Найти секцию «Privacy»</li><li>Снять галочку «Allow GitHub to use my data for AI model training»</li><li>Сохранить настройки</li></ol><p>Можно сделать opt-out и после 24 апреля, но GitHub предупреждает: с этого момента сбор прекращается, но ранее собранные данные из обучающих датасетов уже не удаляются. Поэтому правильная последовательность — сначала отключить, потом проверить, потом продолжать работу.</p><p>У Copilot есть и другие настройки приватности, которые стоит просмотреть за один раз: ограничение на приватные репозитории, телеметрия редактора, история чата. Подробности — в <a href="https://docs.github.com/copilot/how-tos/manage-your-account/managing-copilot-policies-as-an-individual-subscriber">документации GitHub</a>.</p><h2>Что думает сообщество</h2><p>Реакция на анонс в обсуждении GitHub Community — преимущественно негативная. Из всех комментариев публично поддержал инициативу только VP developer relations Martin Woodward — остальные напоминают, что Codex в основе Copilot и так обучен на публичном коде GitHub без разрешения авторов, и критикуют сам паттерн opt-out. Европейская норма противоположная: opt-in, то есть по умолчанию данные не собираются, а пользователь должен явно согласиться.</p><blockquote>Политика соответствует принятым в индустрии практикам и улучшит производительность модели для всех пользователей. Участвуя, вы помогаете нашим моделям лучше понимать рабочие процессы разработки, предлагать более точный и безопасный код и находить потенциальные баги до попадания в прод.</blockquote><p>В FAQ GitHub ссылается на то, что Anthropic, JetBrains и Microsoft ведут похожие политики. Это правда — у большинства крупных ИИ-продуктов opt-out, а не opt-in. Но для разработчиков, которые хранят в приватных репозиториях коммерческий код, это не очень утешительный аргумент: прецедент важнее отраслевого консенсуса.</p><h2>Что это значит для российских разработчиков</h2><p>Прямого ограничения GitHub Copilot в России нет — инструмент формально работает, хотя оплата Pro-подписки из РФ затруднена. Российские разработчики массово пользуются Copilot Free и Pro через зарубежные платёжные сервисы или триал-подписки — и именно они сейчас в группе риска. Если вы работаете с коммерческим или чувствительным кодом через личный GitHub-аккаунт — opt-out нужен в первую очередь вам, потому что корпоративного договора, который защитит от передачи данных, у вас нет.</p><p>Альтернативы, которые не передают код в обучение по умолчанию:</p><ul><li><a href="https://www.cursor.com/">Cursor</a> — платные тарифы не используют ваш код для обучения (privacy mode)</li><li><a href="https://codeium.com/">Codeium</a> и Windsurf — у Enterprise-тарифа обучение на коде отключено</li><li><a href="https://tabby-inc.com/">Tabby</a> и self-hosted аналоги — локальное выполнение, код не покидает сеть</li><li>Qwen3.6-35B-A3B и аналогичные open-weight модели через vLLM или Ollama — полностью локально, без третьих сторон</li></ul><h2>Что в итоге</h2><p>Изменение политики GitHub — не просто мелкий тумблер в настройках. Это разворот дефолта: раньше, чтобы отдать код на обучение, нужно было согласиться, теперь — нужно отказаться. Для публичных проектов это слабо что меняет. Для приватных — вопрос того, что именно уходит в тренировочный датасет Microsoft. Есть и обратная сторона: если все массово сделают opt-out, Copilot будет хуже догонять конкурентов — но это забота GitHub, а не разработчика, которому нужно защитить код клиента.</p><p>Если вы используете Copilot Free, Pro или Pro+ — сегодня самое время открыть <a href="https://github.com/settings/copilot/features">github.com/settings/copilot/features</a> и снять галочку. Займёт минуту; гарантирует, что ваш коммерческий код не уйдёт в тренировочный датасет Microsoft.</p><p>Политику разбирает <a href="https://www.theregister.com/2026/03/26/github_ai_training_policy_changes/">The Register</a>, официальные детали — в <a href="https://github.blog/news-insights/company-news/updates-to-github-copilot-interaction-data-usage-policy/">анонсе GitHub Blog</a> и <a href="https://github.com/orgs/community/discussions/188488">FAQ в GitHub Community</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Anthropic запустила Claude Design — ИИ собирает прототипы, слайды и лендинги в диалоге</title>
      <link>https://tproger.ru/news/anthropic-zapustila-claude-design-ii-sobiraet-prototipy-slajd</link>
      <comments>https://tproger.ru/news/anthropic-zapustila-claude-design-ii-sobiraet-prototipy-slajd?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/anthropic-zapustila-claude-design-ii-sobiraet-prototipy-slajd</guid>
      <description><![CDATA[<p>Anthropic запустила Claude Design на Opus 4.7: прототипы, слайды и лендинги в диалоге, с handoff в Claude Code. Разбираем, что умеет.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/anthropic-zapustila-claude-design-ii-sobiraet-prototipy-slajd">Anthropic запустила Claude Design — ИИ собирает прототипы, слайды и лендинги в диалоге</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 18 Apr 2026 09:15:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если вам нужно быстро собрать прототип интерфейса, питч-дек или лендинг, но вы не дизайнер — теперь это можно делать в диалоге с Claude. Anthropic Labs 17 апреля запустила <a href="https://www.anthropic.com/news/claude-design-anthropic-labs">Claude Design</a>: отдельный продукт внутри экосистемы Claude, который собирает макеты, прототипы, слайды и маркетинговые ассеты по текстовому описанию и дальше правится голосом, комментариями и ползунками.</p><p>Claude Design работает на Claude Opus 4.7 — самой мощной мультимодальной модели Anthropic на сегодня. Он доступен в research preview (ранний доступ с возможными ограничениями) всем подписчикам Claude Pro, Max, Team и Enterprise на <a href="https://claude.ai/design">claude.ai/design</a>.</p><ul><li>Новый продукт Anthropic Labs для визуального дизайна через диалог с Claude</li><li>Под капотом — Claude Opus 4.7, сильнейшая vision-модель Anthropic</li><li>Доступен в research preview для Claude Pro, Max, Team и Enterprise</li><li>Поддерживает прототипы, слайды (PPTX, Canva), лендинги и макеты</li><li>Читает кодовую базу и дизайн-файлы, применяет фирменные стили автоматически</li><li>Handoff в Claude Code — передаёт готовый прототип на реализацию одной командой</li></ul><h2>Что такое Claude Design</h2><p>Это не замена Figma или Canva, а слой над Claude, который превращает разговор в визуальный артефакт. Вы пишете «сделай лендинг для SaaS-продукта в нашем стиле с тремя тарифами и FAQ» — Claude собирает первую версию. Дальше её можно редактировать тремя способами: кликнуть на элемент и оставить комментарий, исправить текст прямо в макете или крутить ползунки. Причём ползунки — не из фиксированного набора: Claude создаёт их под задачу, «от этой версии к той версии» или «от плотного к воздушному».</p><p>Claude собирает фирменный стиль не из абстрактного описания. При настройке команда подключает свою кодовую базу и дизайн-файлы — модель вытаскивает оттуда цвета, типографику, компоненты и применяет их к каждому новому проекту. У команды может быть несколько систем одновременно — например, отдельная для B2B-продукта и отдельная для маркетинга.</p><h2>Что можно делать</h2><ul><li><b>Интерактивные прототипы.</b> Дизайнер превращает статичный макет в кликабельный прототип без PR-а и ревью кода</li><li><b>Wireframes и mockups.</b> Продакт рисует флоу фичи и передаёт их в Claude Code на имплементацию или дизайнеру на доработку</li><li><b>Design explorations.</b> Дизайнер за одну сессию получает десяток разных направлений макета, а не два-три из-за нехватки времени</li><li><b>Питч-деки и презентации.</b> Основатель превращает черновик в готовую on-brand презентацию и экспортирует её в PPTX или в Canva</li><li><b>Маркетинговые ассеты.</b> Маркетолог делает лендинги, соцсети, креативы для кампаний и отдаёт дизайнеру на полировку</li><li><b>Прототипы нового поколения.</b> Макеты со встроенным кодом: голосовой ввод, видео, шейдеры, 3D и ИИ — то, что обычно рисуют через Three.js и вложенные iframe</li></ul><h2>Как это работает внутри</h2><p>Импорт материалов гибкий. Можно начать с текстового промпта, загрузить картинки, DOCX, PPTX или XLSX, показать Claude репозиторий или дать ему «поймать» элемент прямо с сайта через web capture tool — чтобы прототип выглядел как настоящий продукт.</p><p>Совместная работа идёт по модели, привычной по Figma и Google Docs: документ может быть приватным, доступным по ссылке всем в организации или с правом редактирования — тогда коллеги могут одновременно править макет и общаться с Claude в групповом чате.</p><p>Экспорт — внутренняя ссылка, папка, Canva, PDF, PPTX или HTML-файл. Самое интересное для разработчиков — <b>handoff в Claude Code</b> (передача макета в кодогенератор): система упаковывает макет, дизайнерский замысел (design intent) и ассеты в bundle, который уходит в Claude Code одной инструкцией. Фронтенду не нужно угадывать отступы и цвета — всё приходит вместе с намерением дизайнера.</p><h2>Что говорят первые пользователи</h2><blockquote>Наши самые сложные страницы, которые в других инструментах требовали 20+ промптов, в Claude Design собираются за 2. Прыжок от прототипа к продакшену через Claude Code стал бесшовным.</blockquote><p>Anthropic отдельно подчёркивает партнёрство с Canva: дизайны из Claude Design переносятся в Canva одним кликом и там превращаются в полностью редактируемые проекты для публикации.</p><h2>Как получить доступ</h2><p>Claude Design входит в существующие подписки Claude: Pro, Max, Team, Enterprise. Дополнительных лицензий покупать не нужно — продукт использует лимиты вашего тарифа.</p><p>У Enterprise-организаций Claude Design по умолчанию выключен — администратор включает его в Organization settings. Это сделано, чтобы команды безопасников успели оценить, что модель читает дизайн-файлы и кодовую базу.</p><p>Рассылка в research preview идёт постепенно: Anthropic раскатывает доступ по подписчикам в течение 17-18 апреля. Стартовая точка — <a href="https://claude.ai/design">claude.ai/design</a>.</p><h2>Что это значит для рынка</h2><p>Claude Design попадает в горячий сегмент «агенты для визуального дизайна и фронта». Прямые конкуренты — <a href="https://v0.dev/">v0 от Vercel</a>, <a href="https://www.figma.com/make/">Figma Make</a>, <a href="https://bolt.new/">Bolt</a>, <a href="https://lovable.dev/">Lovable</a>, Framer AI. Отличие Anthropic — ставка на интеграцию с Claude Code: прототип уходит в агентного кодогенератора в ту же минуту, в том же разговоре.</p><p>Анонс взлетел в топ Hacker News — и ключевая история там не про «ещё один ИИ-дизайнер», а про «ещё один слой для Claude Code, который закрывает дыру на дизайнерской стороне». То есть сообщество реагирует в первую очередь на handoff-сценарий, а не на генерацию макетов.</p><h2>Что в итоге</h2><p>Claude Design — не «ещё один AI для картинок», а попытка Anthropic закрыть рабочий стык между дизайном и кодом. Для команд, которые уже сидят на Claude, это означает меньше инструментов и меньше handoff-а: макет рождается, обсуждается и уходит в реализацию в одном окне.</p><p>Для российских разработчиков и продактов, у которых уже есть доступ к Claude, это сокращает путь от идеи до прототипа — без переключения между инструментами и форматами. Дизайнерам же Claude Design — повод сместить фокус с рутинного прототипирования к системному дизайну и фирменным стилям, которые ИИ пока не придумывает самостоятельно.</p><p>Источники: <a href="https://www.anthropic.com/news/claude-design-anthropic-labs">анонс Anthropic Labs</a>, <a href="https://news.ycombinator.com/item?id=46115600">обсуждение на Hacker News</a>, страница продукта <a href="https://claude.ai/design">claude.ai/design</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Alibaba открыла веса Qwen3.6-35B-A3B — MoE-модель с 1М контекста для локальных ИИ-агентов</title>
      <link>https://tproger.ru/news/alibaba-otkryla-vesa-qwen3-6-35b-a3b-moe-model-s-1m-konteksta</link>
      <comments>https://tproger.ru/news/alibaba-otkryla-vesa-qwen3-6-35b-a3b-moe-model-s-1m-konteksta?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/alibaba-otkryla-vesa-qwen3-6-35b-a3b-moe-model-s-1m-konteksta</guid>
      <description><![CDATA[<p>Alibaba открыла веса Qwen3.6-35B-A3B — MoE на 3 млрд активных параметров, контекст 1 млн токенов. Разбираем архитектуру и как подключить к Claude Code.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/alibaba-otkryla-vesa-qwen3-6-35b-a3b-moe-model-s-1m-konteksta">Alibaba открыла веса Qwen3.6-35B-A3B — MoE-модель с 1М контекста для локальных ИИ-агентов</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 18 Apr 2026 08:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Если у вас на столе стоит машина с RTX 4090 или серверным GPU, а в кармане кончаются кредиты на Claude Code или Cursor — теперь можно подключить их к локальному агенту. Alibaba выложила веса <b>Qwen3.6-35B-A3B</b> на <a href="https://huggingface.co/Qwen/Qwen3.6-35B-A3B">Hugging Face</a> — это первая open-weight модель из линейки Qwen3.6.</p><p>В марте Alibaba уже выпустила <a href="https://tproger.ru/news/alibaba-vypustila-qwen3-6-plus---ii-model-dlya-agentnogo-kodinga">Qwen3.6-Plus</a> — закрытую flagship-модель с API-доступом через OpenRouter. Теперь у серии появилась открытая ветка: архитектура Mixture of Experts, 35 млрд параметров всего, но при генерации токена активируются только 3 млрд. Поэтому модель можно запустить на одной современной видеокарте — правда, с квантизацией до 4 бит, о требованиях подробнее ниже.</p><p>Саймон Уиллисон (создатель Datasette и автор утилиты llm) <a href="https://simonwillison.net/2026/Apr/16/qwen-beats-opus/">отметил</a>, что Qwen3.6-35B-A3B в его популярном тесте «pelican on a bicycle» сгенерировала SVG-иллюстрацию лучше, чем свежий закрытый Claude Opus 4.7 от Anthropic. Уиллисон гонял квантизованную версию модели (~21 ГБ) на MacBook Pro M5 через LM Studio. Тест полушутливый, но показательный: open-weight-ветка догоняет топ-проприетарные.</p><ul><li>Первая open-weight модель линейки Qwen3.6 под лицензией Apache 2.0</li><li>Архитектура MoE: 35 млрд параметров всего, из 256 экспертов активны 8 роутящихся + 1 общий (всего 3 млрд параметров на токен)</li><li>Нативный контекст 262 144 токена, через YaRN-растяжение — до 1 010 000</li><li>Мультимодальная: текст + картинки (Vision Language Model)</li><li>На Terminal-Bench 2.0 — 51,5 балла, прирост +11 относительно предшественника Qwen3.5-35B-A3B</li><li>Работает через vLLM, SGLang, KTransformers и Hugging Face Transformers</li></ul><h2>Что внутри: MoE и гибридные слои</h2><p>Qwen3.6-35B-A3B — это Mixture of Experts. Суффикс <b>A3B</b> в названии означает <i>Active 3 Billion</i>: при генерации каждого токена модель выбирает 8 «экспертов» из 256 плюс один общий, суммарно 3 млрд активных параметров из 35 млрд.</p><p>Архитектура гибридная: 10 повторяющихся блоков, в каждом три последовательности Gated DeltaNet → MoE и одна — Gated Attention → MoE. Gated DeltaNet — вариант линейного внимания, он даёт быструю обработку длинного контекста. Gated Attention с полным квадратичным вниманием стоит там, где линейная аппроксимация теряет точность.</p><p>Контекст нативно 262 144 токена. Для длинных сессий (до 1 010 000 токенов) Alibaba рекомендует YaRN-растяжение RoPE, иначе модель теряет фокус на хвосте.</p><h2>Что нового относительно Qwen3.5</h2><p>В карточке модели Alibaba выделяет две главные линии улучшений.</p><p><b>Agentic Coding.</b> Модель лучше держит фронтенд-воркфлоу и рассуждает про репозиторий целиком, а не по отдельным файлам. По внутренним бенчмаркам Alibaba — прирост на коде относительно Qwen3.5-35B-A3B:</p><ul><li>Terminal-Bench 2.0: 51,5 против 40,5 — +11 баллов</li><li>SWE-bench Verified: 73,4 против 70,0</li><li>SWE-bench Pro: 49,5 против 44,6</li><li>QwenWebBench (фронтенд-генерация, Elo): 1397 против 978</li></ul><p><b>Thinking Preservation.</b> В Qwen3.5 reasoning-токены модели не сохранялись между шагами диалога — модель каждый раз начинала рассуждать заново. В 3.6 появилась опция оставлять reasoning-контекст в истории сообщений, она включается через параметр сэмплера. Для многошаговых агентных сценариев это означает, что промежуточные выводы переходят между шагами, и агент не теряет «почему» на шаге 5, если оно возникло на шаге 2.</p><h2>Как читает картинки</h2><p>Qwen3.6-35B-A3B — Vision Language Model, визуальный энкодер встроен в саму модель. По внутренним замерам Alibaba, на большинстве визуальных бенчмарков модель обгоняет закрытый Claude Sonnet 4.5 — включая MMMU, Mathvista, RealWorldQA и HallusionBench. Самые заметные разрывы:</p><ul><li>RealWorldQA — 85,3 vs 70,3 у Claude Sonnet 4.5</li><li>HallusionBench (устойчивость к галлюцинациям на картинках) — 69,8 vs 59,9 у Claude Sonnet 4.5</li></ul><p>Для агентов, которые читают скриншоты интерфейсов или диаграммы из документации, это важное сочетание: open-weight плюс конкурентоспособное визуальное разумение. Раньше открытых моделей такого уровня на визуальных задачах почти не было.</p><h2>Как запустить локально</h2><p>Официально поддерживаются четыре способа инференса: Hugging Face Transformers, vLLM, SGLang и KTransformers. Для локального сервера с OpenAI-совместимым API проще всего взять vLLM:</p><p>Требования к железу определяются активной частью модели — 3 млрд параметров. В BF16 модель целиком занимает около 70 ГБ VRAM, но с квантизацией (AWQ/GPTQ 4-бит) и FlashAttention инференс помещается на одну RTX 4090 (24 ГБ) или L40S (48 ГБ). Для полного контекста 1 млн токенов и пакетной обработки лучше две карты или серверный A100/H100.</p><p>После старта vLLM модель принимает запросы по OpenAI-совместимому API на http://localhost:8000/v1. В Aider и Cursor этот URL подставляется напрямую. Claude Code ждёт Anthropic-совместимый протокол, поэтому для него нужен прокси — например, <a href="https://github.com/musistudio/claude-code-router">claude-code-router</a>, он конвертирует OpenAI-ответы vLLM в формат Anthropic. Модель поддерживает нативный function calling, так что работать она будет как полноценный агент, а не как plain-LLM.</p><h2>Для кого это</h2><ul><li>Команды с чувствительным кодом, которым нельзя отправлять репозиторий в сторонние API</li><li>Разработчики, которые хотят независимость от биллинга и лимитов Claude, OpenAI, Cursor</li><li>Российские пользователи: модель скачивается с Hugging Face без VPN и санкционных рисков, лицензия Apache 2.0</li><li>Исследователи — для файн-тюнинга, дистилляции и экспериментов с ин-контекстным обучением</li></ul><h2>Что это значит</h2><p>Qwen3.6-35B-A3B — одно из крупных open-weight событий апреля 2026 года. В связке с vLLM модель можно подключить к Aider, Cursor или Claude Code через прокси и получить автономного кодового агента на собственном железе — без биллинга Anthropic или OpenAI. А в визуальном разумении она ещё и обгоняет закрытый Claude Sonnet 4.5.</p><p>Главный вывод: открытая ветка Qwen 3.6 готова к реальной агентной работе — не как игрушка, а как замена топ-проприетарных моделей в части задач.</p><p>Источники: <a href="https://huggingface.co/Qwen/Qwen3.6-35B-A3B">карточка модели на Hugging Face</a>, заметка <a href="https://simonwillison.net/2026/Apr/16/qwen-beats-opus/">Саймона Уиллисона от 16 апреля</a>, наш материал о закрытой <a href="https://tproger.ru/news/alibaba-vypustila-qwen3-6-plus---ii-model-dlya-agentnogo-kodinga">Qwen3.6-Plus</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Alibaba выпустила Qwen3.6-Plus — ИИ-модель для агентного кодинга с контекстом в 1 млн токенов</title>
      <link>https://tproger.ru/news/alibaba-vypustila-qwen3-6-plus---ii-model-dlya-agentnogo-kodinga</link>
      <comments>https://tproger.ru/news/alibaba-vypustila-qwen3-6-plus---ii-model-dlya-agentnogo-kodinga?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/alibaba-vypustila-qwen3-6-plus---ii-model-dlya-agentnogo-kodinga</guid>
      <description><![CDATA[<p>Alibaba выпустила Qwen3.6-Plus с контекстом 1 млн токенов. Обгоняет Claude Opus на бенчмарках Terminal-Bench и OmniDocBench. Бесплатна на OpenRouter.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/alibaba-vypustila-qwen3-6-plus---ii-model-dlya-agentnogo-kodinga">Alibaba выпустила Qwen3.6-Plus — ИИ-модель для агентного кодинга с контекстом в 1 млн токенов</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Apr 2026 16:23:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Alibaba выпустила <a href="https://openrouter.ai/qwen/qwen3.6-plus-preview">Qwen3.6-Plus</a> — новую языковую модель, ориентированную на агентные задачи: автономное написание кода, работу с длинными документами и многошаговые сценарии. Модель доступна бесплатно в preview-режиме на OpenRouter.</p><p>Qwen3.6-Plus поддерживает контекстное окно в 1 млн токенов (около 2000 страниц текста) и генерирует до 65 536 выходных токенов. По ранним тестам, модель работает по замерам пользователей OpenRouter, примерно в 3 раза быстрее Claude Opus 4.5.</p><ul><li>Qwen3.6-Plus — новая модель Alibaba для агентного кодинга и длинных документов</li><li>Контекст 1 млн токенов, до 65 536 выходных токенов</li><li>Поддерживает режим цепочечного мышления (thinking mode) и нативный вызов функций</li><li>Обгоняет Claude 4.5 Opus на Terminal-Bench 2.0 (61,6 vs 59,3) и OmniDocBench (91,2 vs 87,7)</li><li>Бесплатна в preview на OpenRouter, но free tier собирает промпты для обучения</li></ul><h2>Бенчмарки: где Qwen3.6-Plus выигрывает</h2><p>По опубликованным результатам Qwen3.6-Plus показывает сильные результаты на агентных и документных бенчмарках:</p><ul><li><b>Terminal-Bench 2.0</b> (агентный кодинг в терминале) — 61,6 vs 59,3 у Claude 4.5 Opus</li><li><b>OmniDocBench v1.5</b> (распознавание документов) — 91,2 vs 87,7 у Claude 4.5 Opus</li><li><b>RealWorldQA</b> (рассуждение по изображениям) — 85,4 vs 77,0 у Claude 4.5 Opus</li><li><b>SWE-bench Verified</b> (исправление багов в реальных репозиториях) — 78,8, уступает Claude 4.5 Opus (80,9)</li></ul><p>Модель поддерживает обработку изображений в контексте документных и аналитических задач, но не предназначена для генерации изображений.</p><h2>Архитектура и скорость</h2><p>Qwen3.6-Plus построена на гибридной архитектуре нового поколения. Ранние пользователи отмечают, что модель «более решительна» в ответах — использует меньше токенов для достижения результата и показывает лучшую надёжность в многошаговых агентных сценариях.</p><p>Скорость генерации — примерно в 3 раза выше, чем у Claude Opus 4.6 по ранним замерам сообщества. Однако time-to-first-token на бесплатном тире составляет в среднем 11,5 секунд, что ощутимо влияет на интерактивные сценарии.</p><h2>Доступность и ограничения</h2><ul><li>Доступна на <a href="https://openrouter.ai/qwen/qwen3.6-plus-preview">OpenRouter</a> бесплатно в preview-режиме</li><li>Бесплатный тир собирает промпты и ответы для обучения — учитывайте при работе с конфиденциальными данными</li><li>Платный API-доступ без сбора данных также доступен</li><li>Нативный вызов функций (function calling) — можно использовать как агента без дополнительных обёрток</li></ul><h2>Выводы</h2><p>Qwen3.6-Plus — сильная модель для агентных задач, особенно в кодинге и работе с документами. Бесплатный preview на OpenRouter подходит для прототипирования и личных проектов, для production — платный API.</p>]]></content:encoded>
    </item>
    <item>
      <title>ASPA — как новый стандарт проверяет путь интернет-трафика и защищает от утечек маршрутов</title>
      <link>https://tproger.ru/translations/aspa---kak-novyj-standart-proveryaet-put-internet-trafika-i-zashhi</link>
      <comments>https://tproger.ru/translations/aspa---kak-novyj-standart-proveryaet-put-internet-trafika-i-zashhi?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/aspa---kak-novyj-standart-proveryaet-put-internet-trafika-i-zashhi</guid>
      <description><![CDATA[<p>Как ASPA дополняет RPKI/ROA, проверяя не только пункт назначения, но и весь путь трафика. Перевод статьи Cloudflare о новом стандарте безопасности BGP.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/aspa---kak-novyj-standart-proveryaet-put-internet-trafika-i-zashhi">ASPA — как новый стандарт проверяет путь интернет-трафика и защищает от утечек маршрутов</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[смарт-контракты]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Apr 2026 12:37:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Это перевод с адаптацией <a href="https://blog.cloudflare.com/aspa-secure-internet/">статьи</a> Mingwei Zhang и Bryton Herdes из блога <a href="https://blog.cloudflare.com/">Cloudflare</a>.</p><p>BGP (Border Gateway Protocol) — протокол, по которому автономные системы (AS) обмениваются информацией о маршрутах. Автономная система — это сеть под управлением одного оператора: провайдера, облачной платформы, крупной компании. Когда вы открываете сайт, трафик проходит через цепочку таких AS. Но иногда он уходит не туда — из-за ошибок конфигурации или злонамеренных действий. Такие инциденты называются утечками маршрутов (route leaks). Новый криптографический стандарт ASPA (Autonomous System Provider Authorization) призван решить эту проблему — он проверяет не только пункт назначения, но и весь путь трафика.</p><ul><li>ASPA — новый стандарт криптографической проверки маршрутов BGP, дополняющий существующий RPKI/ROA</li><li>ROA проверяет пункт назначения трафика, ASPA проверяет путь — цепочку сетей, через которые он проходит</li><li>ASPA обнаруживает утечки маршрутов, проверяя, что трафик движется по «горной» модели: вверх к провайдеру, через пиринг, вниз к получателю</li><li>Создать ASPA-запись можно за пару кликов в RIPE или ARIN</li><li>Cloudflare Radar теперь отслеживает глобальное внедрение ASPA в реальном времени</li></ul><h2>Как сейчас защищён BGP: RPKI и ROA</h2><p>Сегодня сети используют инфраструктуру <a href="https://blog.cloudflare.com/rpki/">RPKI</a> (Resource Public Key Infrastructure), внедрение которой значительно выросло за последние годы. В рамках RPKI сети публикуют криптографические записи — ROA (Route Origin Authorizations). ROA — это верифицируемое цифровое удостоверение, подтверждающее, что конкретная автономная система (AS) авторизована анонсировать определённые IP-адреса. Это решает проблему origin hijacks — когда одна сеть выдаёт себя за другую.</p><p>Но ROA проверяет только пункт назначения. А что насчёт пути?</p><h2>Что такое ASPA и как он работает</h2><p>ASPA (Autonomous System Provider Authorization) надстраивается над RPKI. Если ROA проверяет <b>куда</b> идёт трафик, то ASPA проверяет <b>как</b> он туда добирается.</p><p>Когда данные путешествуют по интернету, каждая сеть на пути записывается в лог — AS_PATH. ASPA позволяет сетям официально публиковать список авторизованных апстрим-провайдеров в системе RPKI. Любая принимающая сеть может посмотреть на AS_PATH, проверить ASPA-записи и убедиться, что трафик прошёл только через одобренную цепочку сетей.</p><h3>Модель «горы»: как устроена здоровая маршрутизация</h3><p>В нормальной топологии (valley-free routing) трафик движется по «горной» модели:</p><ol><li><b>Подъём (Up-Ramp)</b> — трафик идёт от клиента вверх через всё более крупных провайдеров (ISP)</li><li><b>Вершина (Apex)</b> — на уровне магистрали трафик может пересечь один пиринговый линк</li><li><b>Спуск (Down-Ramp)</b> — трафик спускается через провайдеров к получателю</li></ol><p>Один из типов утечки — «долина» в этой модели. Она происходит, когда трафик спускается к клиенту и неожиданно пытается снова подняться к другому провайдеру. Клиентские сети не предназначены для транзита трафика между крупными провайдерами.</p><h3>Как ASPA валидирует маршруты</h3><p>ASPA проверяет цепочку отношений с обоих концов маршрута:</p><ol><li><b>Проверка подъёма</b> — начинаем от источника и двигаемся вперёд. На каждом хопе спрашиваем: «Авторизовала ли эта сеть следующую как своего провайдера?»</li><li><b>Проверка спуска</b> — то же самое, но в обратном направлении от получателя BGP-обновления</li></ol><p>Если «подъём» и «спуск» встречаются или пересекаются на вершине — маршрут <b>валидный</b>. Форма горы сохранена.</p><p>Если пути не сходятся и в середине есть разрыв — ASPA отмечает маршрут как проблемный. Этот разрыв и есть «долина», то есть утечка.</p><h3>Пример: обнаружение утечки маршрута</h3><p>Допустим, сеть AS65539 получает подозрительный маршрут от клиента AS65538. Клиент пытается отправить трафик, полученный от одного провайдера (AS65537), «вверх» к другому провайдеру (AS65539) — действуя как мост между провайдерами. Это классическая утечка маршрута.</p><p>Процесс валидации ASPA:</p><ol><li>Проверяем подъём: источник (AS65536) авторизует своего провайдера — проверка пройдена</li><li>Проверяем спуск: начинаем от получателя и смотрим назад — видим клиента (AS65538)</li><li>Несовпадение: подъём заканчивается на AS65537, спуск — на AS65538. Пути не соединяются</li></ol><p>Результат: маршрут отмечен как <b>ASPA Invalid</b>. Без подписанных ASPA-объектов в RPKI невозможно определить, какие сети авторизованы анонсировать префиксы горизонтально (пирам) или вверх (провайдерам).</p><h2>ASPA против forged-origin hijacks</h2><p>ASPA эффективно защищает от forged-origin hijacks — атак, при которых злоумышленник обходит проверку ROV: он анонсирует реальный IP-префикс с правильным origin AS, но вставляет себя в AS_PATH как промежуточный хоп. ROV это не замечает, так как проверяет только origin. Источник формально правильный, но связь между атакующим и жертвой — сфабрикована.</p><p>ASPA разоблачает это: сеть-жертва криптографически объявляет своих реальных провайдеров, и поскольку атакующий не входит в этот список, маршрут отклоняется.</p><p><b>Ограничение:</b> ASPA не защищает от всех случаев. Провайдер может подделать пиринговый линк с другой AS, чтобы привлечь трафик клиента коротким AS_PATH, даже если такого пирингового линка в реальности не существует. ASPA работает только с информацией о провайдерах и ничего не знает о пиринговых отношениях.</p><h2>Как создать ASPA-запись: пара кликов</h2><p>Создание ASPA-объекта для вашей сети стало простым в реестрах <a href="https://www.ripe.net/">RIPE</a> и <a href="https://www.arin.net/">ARIN</a>. Всё, что нужно — ваш номер AS и номера AS ваших провайдеров, у которых вы покупаете транзит. В обратном направлении это сети, которым вы разрешаете присылать полную таблицу маршрутизации — карту достижимости остального интернета.</p><p>Пример Cloudflare: для AS203898 (офис в Лондоне) с тремя интернет-провайдерами (AS8220, AS2860, AS1273) процесс занимает три шага:</p><ol><li>Войти в RPKI-дашборд RIPE и перейти в раздел ASPA</li><li>Нажать «Create ASPA» для нужного AS</li><li>Указать список провайдеров</li></ol><p>Через короткое время ASPA-запись появляется в глобальной RPKI-экосистеме.</p><p>Отдельный случай — запись с <b>AS0</b> в качестве провайдера. Это означает, что у сети нет апстрим-провайдеров. По определению, каждая транзитно-свободная сеть Tier-1 в будущем должна будет подписать ASPA только с «AS0» — если у неё действительно только пиринговые и клиентские отношения.</p><h2>Мониторинг ASPA в Cloudflare Radar</h2><p><a href="https://radar.cloudflare.com/">Cloudflare Radar</a> добавил новые возможности мониторинга внедрения ASPA:</p><ul><li>Графики роста внедрения ASPA по пяти региональным реестрам (RIR)</li><li>Интеграция ASPA-данных в страницы маршрутизации по странам и ASN</li><li>Для каждой AS — список провайдеров из ASPA-объекта, проверка BGP-апстримов и история изменений</li></ul><h2>Что нужно для полного внедрения ASPA</h2><p>ASPA — это криптографический фундамент для валидации путей. Но, как показал опыт RPKI/ROA, внедрение займёт время. Необходимы обновления:</p><ul><li>RPKI Relying Party (RP) — программы проверки криптографических записей</li><li>RTR (RPKI-to-Router protocol) — протокол передачи данных маршрутизаторам</li><li>BGP-реализации на маршрутизаторах — для валидации путей с использованием ASPA</li></ul><p>Помимо создания ASPA-записей, операторам рекомендуется настроить <b>BGP roles</b> (<a href="https://www.rfc-editor.org/rfc/rfc9234">RFC 9234</a>). BGP roles привязывают предполагаемые отношения между AS к конкретным BGP-сессиям, помогая ASPA определить, какой алгоритм валидации применять — для апстрима или даунстрима. Уточните у своих вендоров оборудования, поддерживают ли они BGP roles и атрибут OTC (Only-to-Customer).</p><blockquote>Управление ASPA-записями не отличается от управления ROA. В будущем, когда сети начнут активно блокировать невалидные пути, пропуск легитимного провайдера может привести к потере трафика. Но это тот же риск, который операторы уже научились контролировать.</blockquote><h2>Выводы</h2><p>ASPA — следующий логичный шаг в безопасности интернет-маршрутизации после RPKI/ROA. Если ROA закрепил за каждой AS право анонсировать свои IP-адреса, то ASPA закрепляет право на транзит — описывает авторизованный путь трафика.</p><p>Для операторов сетей: <a href="https://radar.cloudflare.com/">проверьте в Cloudflare Radar</a>, есть ли у вашей AS ASPA-запись, и создайте её в RIPE или ARIN. Чем больше сетей подпишут ASPA, тем быстрее стандарт начнёт реально блокировать утечки маршрутов.</p>]]></content:encoded>
    </item>
    <item>
      <title>Переосмысление пулл-реквестов: почему code review должен учить, а не только ловить баги</title>
      <link>https://tproger.ru/translations/pereosmyslenie-pull-rekvestov--pochemu-code-review-dolzhen-uchit--</link>
      <comments>https://tproger.ru/translations/pereosmyslenie-pull-rekvestov--pochemu-code-review-dolzhen-uchit--?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Евгений Стребков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/pereosmyslenie-pull-rekvestov--pochemu-code-review-dolzhen-uchit--</guid>
      <description><![CDATA[<p>Как стековые PR, приоритет файлов в диффе и единая страница ревью сокращают comprehension debt. Перевод статьи создателя Lubeno о будущем code review.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/pereosmyslenie-pull-rekvestov--pochemu-code-review-dolzhen-uchit--">Переосмысление пулл-реквестов: почему code review должен учить, а не только ловить баги</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 02 Apr 2026 12:28:46 GMT</pubDate>
      <content:encoded><![CDATA[<p>Это перевод с адаптацией <a href="https://lubeno.dev/blog/reinventing-the-pull-request">статьи</a> Bernard Kolobara, автора платформы <a href="https://lubeno.dev">Lubeno</a>.</p><p>Пулл-реквесты задумывались как инструмент для совместной работы над кодом. На практике они превратились в бюрократическую процедуру: гигантские диффы, комментарии в разных вкладках, коллапсированные «outdated»-ветки обсуждений. Bernard Kolobara считает, что проблема не в самой идее code review, а в инструментах — и предлагает конкретные решения.</p><ul><li>Comprehension debt (долг понимания) — главная проблема, которую должен решать code review, а не только ловля багов</li><li>Стековые пулл-реквесты: разбиение изменений на мелкие самодостаточные коммиты, которые ревьюятся независимо</li><li>Приоритет файлов: тесты и зависимости показываются первыми в диффе через .gitattributes</li><li>Всё на одной странице: код и обсуждение вместе, без вкладок</li><li>ИИ не убил code review — он обнажил хрупкость инструментов, которые не справляются с растущим объёмом кода</li></ul><h2>Comprehension debt: долг понимания кода</h2><p>Addy Osmani <a href="https://addyosmani.com/blog/comprehension-debt/">описал</a> comprehension debt как разрыв между объёмом кода в проекте и тем, сколько из этого кода команда реально понимает. Чем больше разрыв — тем медленнее работа. Проектировать систему, которую полностью понимаешь, проще, чем ту, которую «примерно знаешь». С появлением ИИ-агентов в рабочих процессах этот разрыв значительно вырос.</p><p>Paul Graham в эссе <a href="https://paulgraham.com/greatwork.html">о великой работе</a> пишет, что лучшие идеи приходят вне клавиатуры — на прогулке, в душе, перед сном. Но для этого «фонового процессинга» нужно, чтобы контекст проекта уже был в голове. Сначала нужна осознанная работа с кодом.</p><p>Code review — идеальный момент для сокращения этого разрыва. Каждый ревью — возможность узнать, как кодовая база развивается, каковы её границы и ограничения. Не обязательно проверять каждую строку дифа — достаточно понять ключевые части, чтобы обновить ментальную модель. Ловля багов — лишь <a href="https://www.davidpoll.com/2026/02/code-review-is-not-about-catching-bugs">одна часть</a> ценности ревью.</p><h2>Как сократить разрыв: конкретные решения</h2><h3>Стековые пулл-реквесты</h3><p>Разбить большой PR на маленькие — самый эффективный способ помочь ревьюеру. В теории для этого подходят коммиты: каждый — самодостаточная единица, <a href="https://youtu.be/GOrKfCs-mr0?si=SWndwJF0IWOF-SG7&amp;t=102">рассказывающая историю</a>. На практике это не работает из-за Git.</p><p>Git заточен под append-only workflow. Вернуться в старый коммит и отредактировать его — мучительно. Даже если вы аккуратно структурировали историю, рано или поздно появляется серия коммитов «fix», «actual fix», «ok now really fix». Когда смотришь на отдельный коммит, не видишь полной картины — правки могут быть размазаны по нескольким коммитам дальше по истории.</p><p>Bernard и его коллега Luísa решают эту проблему с помощью <a href="https://docs.jj-vcs.dev/latest/">Jujutsu</a> — VCS, которая позволяет прыгнуть в любой коммит и отредактировать его на месте, а затем автоматически пропагирует изменения через всю историю. Это позволяет «вылепить» идеальную историю коммитов.</p><p>В Lubeno стековые PR детектятся автоматически. Каждый PR показывается как дифф относительно родительского PR — ревью и одобрение идут независимо. Автор не ждёт, пока предыдущий PR смёржат, и может продолжать работу.</p><h3>Приоритет файлов в диффе</h3><p>При ревью автор первым делом смотрит тесты: были ли изменены существующие, тестируют ли новые что-то полезное. Затем — зависимости: добавляется ли новая и зачем. Но все платформы для code review показывают файлы в алфавитном порядке — важные изменения легко пропустить в большом диффе.</p><p>Lubeno использует кастомный атрибут priority в .gitattributes (это не стандартный атрибут Git, а расширение Lubeno):</p><p>Файлы с высоким приоритетом показываются первыми на странице PR. Простое решение, но оно гарантирует, что самые важные изменения вы увидите сразу.</p><h3>Всё на одной странице</h3><p>Почти все платформы для code review разносят комментарии, коммиты и код по разным вкладкам. Автор признаётся: «Я нажимал на вкладку Commits только по ошибке — ни разу намеренно». Обсуждение тесно связано с кодом, но чтобы следить за ним, приходится постоянно скроллить наверх, переключать вкладки и искать нужный фрагмент.</p><p>В Lubeno нет вкладок на странице PR. Код и обсуждение живут в одном месте.</p><h2>Эволюция кода в рамках PR</h2><p>PR — не статичная сущность. Это место для обсуждения и итераций. Знакомая ситуация: вы оставили комментарий, вернулись — и обнаружили несколько новых коммитов, а все ваши комментарии свёрнуты как «outdated». Непонятно, учтены ли ваши замечания, что изменилось с момента вашего последнего просмотра.</p><p>Lubeno отслеживает комментарии к коду и наложение interdiff (разницу между версиями файла в рамках PR): если код был изменён более поздним коммитом (или force push), разница видна прямо в контексте комментария. Не нужно переключаться между вкладками, чтобы понять, что произошло.</p><h2>Будущее пулл-реквестов</h2><p>Некоторые <a href="https://boristane.com/blog/the-software-development-lifecycle-is-dead/">называют</a> PR «реликтом прошлого» и призывают от них отказаться. Автор не согласен: процесс ревью кода сегодня ценнее, чем когда-либо. ИИ не убил code review — он обнажил хрупкость инструментов. Больше строк кода просто сделали их непригодными.</p><p>Code review находится в идеальной точке жизненного цикла: после всей творческой работы и прямо перед продакшном. Это последний шанс разобраться, что именно поедет к пользователям. Если вы хотите создавать продукт, который радует пользователей, вам нужно понимать, что происходит в коде.</p><h2>Выводы</h2><p>Статья Bernard Kolobara — не абстрактные размышления, а описание конкретных решений, реализованных в <a href="https://lubeno.dev">Lubeno</a>. Даже если вы не планируете переходить с GitHub — идеи стековых PR, приоритета файлов и unified-страницы ревью стоит примерить к своему workflow.</p><p>Ключевой тезис: code review — не бюрократия, а инструмент обучения. Каждый ревью должен делать команду чуть умнее, а не просто ставить галочку.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как случайно написать самый быстрый CSV-парсер на C#</title>
      <link>https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-</link>
      <comments>https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-</guid>
      <description><![CDATA[<p>Как SIMD-инструкции SSE2 и AVX2 ускоряют парсинг CSV в 10 раз. Разбираем путь от наивной реализации до библиотеки, обогнавшей Sep в 41 из 70 бенчмарков.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/kak-sluchajno-napisat-samyj-bystryj-csv-parser-na-c-">Как случайно написать самый быстрый CSV-парсер на C#</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[C#]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:28:21 GMT</pubDate>
      <content:encoded><![CDATA[<p>На рождественских каникулах 2025 года автор библиотеки <a href="https://github.com/bbepis/FourLambda.Csv">FourLambda.Csv</a> ехал в автобусе и читал про UTF-8. Обнаружив, что все ASCII-символы в UTF-8 занимают ровно один байт, он начал экспериментировать с подсчётом символов на максимальной скорости. Через несколько дней у него был готовый CSV-парсер на C#, который обогнал все существующие библиотеки в большинстве бенчмарков. Вот как это произошло.</p><blockquote><b>Ключевые выводы</b><br />- SIMD-инструкции (SSE2, AVX2) позволяют обрабатывать 16-32 байта за одну операцию<br />- В UTF-8 все управляющие символы CSV (запятая, кавычка, перенос строки) занимают ровно один байт и совпадают с ASCII<br />- Минималистичный код (16 КБ) побеждает оптимизированный, но раздутый (163 КБ) за счёт давления на L1-кэш процессора<br />- Компилятор и рантайм умеют оптимизировать лучше, если им не мешать микрооптимизациями</blockquote><h2>Наивная реализация — считаем запятые</h2><p>Прежде чем строить парсер, поставим простую задачу: как быстрее всего подсчитать, сколько раз символ , встречается в большом UTF-8-файле? Тестируем на CSV-файле размером 500 МБ.</p><h3>Базовая реализация: 135,305 мс</h3><p>Простейший вариант: перебираем каждый байт и сравниваем с искомым. Можно ли лучше?</p><h3>Unsafe-указатели: 128,683 мс</h3><p>Каждый раз, когда вы обращаетесь к массиву через data[i], рантайм .NET выполняет проверку границ (bounds checking) — убеждается, что индекс не выходит за пределы массива. Если вместо этого использовать unsafe-указатели, мы обещаем рантайму, что не выйдем за границы. Экономия — 7 мс.</p><h3>Развёртка цикла (unrolling 4x): 105,320 мс</h3><p>Механика цикла for обманчиво дорога: на каждой итерации нужно проверить условие выхода и выполнить переход в начало. Если обрабатывать по 4 элемента за итерацию, накладные расходы снижаются в четыре раза. «Хвост» данных, не кратный четырём, обрабатывается обычным побайтовым циклом.</p><h2>Проблема — UTF-8 и Unicode ломают всё</h2><p>Чтобы понять, почему мы можем сканировать текст <i>настолько</i> быстро, нужно разобраться, как текст хранится в памяти компьютера.</p><p>Наименьшая единица данных — байт (числа 0-255). Но человечество использует гораздо больше 256 символов. Ранние системы решали это через кодовые страницы — у каждого региона была своя таблица. Японские системы использовали Shift-JIS, в Китае по закону обязателен GB 18030. Если открыть файл в неправильной кодировке, вы увидите «крокозябры» (mojibake) — бессмысленный набор символов.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/1dd24e6b-e5ac-4bc8-986c-9c0566debde9.webp" alt="Пример mojibake — крокозябры при открытии файла в неправильной кодировке" /><figcaption>Так выглядит mojibake — результат открытия файла в неправильной кодировке</figcaption></figure><p>Unicode решил эту проблему в 90-х: каждому символу присваивается номер (codepoint), а разные кодировки (UTF-8, UTF-16, UTF-32) определяют, как эти номера записываются в байты.</p><p>Вот как UTF-8 кодирует символы в зависимости от количества байтов:</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/09bc69c0-ede8-4534-a016-3d01c7f7357f.webp" alt="Таблица ASCII — первые 128 символов, которые в UTF-8 кодируются одним байтом" /><figcaption>Таблица ASCII-символов: все они кодируются в UTF-8 ровно одним байтом</figcaption></figure><p>Ключевой факт: байты 0x00-0x7F в UTF-8 <b>полностью совпадают</b> с ASCII. Это значит, что управляющие символы CSV — запятая (,), кавычка ("), перевод строки (\n) — всегда занимают ровно один байт, независимо от того, какие Unicode-символы находятся в данных. И это можно использовать для невероятно быстрого сканирования.</p><h2>SIMD — ускорение через параллельные инструкции</h2><p>До сих пор мы обрабатывали по одному байту за раз. Представьте сложение двух массивов по 8 элементов — нужно 8 отдельных операций сложения. Можно распараллелить это по потокам, но управление потоками создаёт накладные расходы и не добавляет эффективности.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/a70a8c10-6a79-472a-b254-0c372fe72b71.webp" alt="Скалярное сложение — один элемент за раз" /><figcaption>Скалярная операция: процессор обрабатывает по одному элементу</figcaption></figure><p><b>SIMD</b> (Single Instruction, Multiple Data) — это набор процессорных инструкций, которые выполняют одну операцию сразу над несколькими элементами данных. Вся магия происходит в железе, поэтому результат приходит за то же время, что и обычная операция над одним элементом.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/1e1ba95a-bfc8-4abe-9db0-6a1d8fb8380a.webp" alt="SIMD — параллельная обработка нескольких элементов" /><figcaption>SIMD: четыре операции сложения за одну инструкцию</figcaption></figure><p>Аналогия: сложить два однозначных числа (8 + 2) — просто. Сложить два семизначных — нужно перебирать разряды. А теперь представьте, что вы можете «увидеть» сумму двух семизначных чисел мгновенно, с той же лёгкостью, что и однозначных. Это и есть SIMD.</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/71aa521a-5fb7-457b-abdd-e169a3fba9ae.webp" alt="Широкий SIMD-регистр обрабатывает ещё больше данных" /><figcaption>Более широкие регистры позволяют обрабатывать больше элементов за такт</figcaption></figure><p>Вот как выглядит SIMD-сложение 4 элементов за одну операцию:</p><p>Мы делаем в 4 раза больше работы за то же усилие.</p><h3>SSE2: 46,663 мс</h3><p>SSE2 (появился в x86-процессорах в 2000 году) работает с 128-битными регистрами — 16 байтов за раз. Алгоритм такой:</p><ol><li>Загрузить 16 байтов данных в SIMD-вектор</li><li>Сравнить каждый байт с искомым (Sse2.CompareEqual) — на позициях совпадений появятся байты 255, на остальных — 0</li><li>Извлечь старшие биты из каждого байта (Sse2.MoveMask) — получаем 16-битное число, где каждый установленный бит означает совпадение</li><li>Подсчитать установленные биты</li></ol><p>Для строки "Hello, I, like, commas." это выглядит так:</p><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/64b6f6ff-d258-444a-9c06-9f382ab70ae9.webp" alt="Поиск разделителей через SIMD — CompareEqual и MoveMask" /><figcaption>SIMD-поиск запятых: сравнение вектора данных с вектором разделителей</figcaption></figure><figure><img src="https://media.tproger.ru/user-uploads/133946/2026-03-28/6f22e456-df00-4a1e-aa15-3becd73f67b1.webp" alt="Расширенная SIMD-диаграмма для строки с Unicode" /><figcaption>Поиск разделителей в строке с Unicode-символами</figcaption></figure><h3>AVX2: 42,220 мс</h3><p>AVX2 (с 2011 года) расширяет регистры до 256 битов — 32 байта за итерацию. Код аналогичен SSE2, но с Vector256 вместо Vector128.</p><p>Прирост скромный — значит, узкое место сместилось на цикл обработки маски. Нужно ускорить подсчёт битов.</p><h3>AVX2 + TZCNT: 16,630 мс</h3><p>BitOperations.TrailingZeroCount — аппаратная инструкция TZCNT, которая мгновенно находит позицию младшего установленного бита. Вместо того чтобы перебирать биты маски по одному, мы прыгаем сразу к следующему совпадению и сбрасываем его.</p><h3>AVX2 + POPCNT: 13,673 мс</h3><p>А есть ещё лучше. BitOperations.PopCount — аппаратная инструкция POPCNT, которая за одну операцию считает количество установленных битов в числе. Именно то, что нам нужно:</p><p>Мы достигли потолка производительности для подсчёта символов. Ускорение в 10 раз по сравнению с наивной реализацией — отличный результат. Пора применить это к парсингу CSV.</p><h2>Поиск разделителей через SIMD</h2><p>При парсинге CSV нас интересуют три управляющих символа: запятая (,), кавычка (") и перевод строки (\n). Правила обработки:</p><ul><li>Запятая — конец текущего поля</li><li>Перевод строки — конец текущей строки</li><li>Кавычка — начало или конец экранированного блока (внутри которого запятые и переносы игнорируются). Две кавычки подряд — это экранированная кавычка в значении</li></ul><p>Парсинг разбивается на два этапа. Первый — определение позиций и длин полей через SIMD (функция DetermineFields). Второй — фактическое извлечение и дeкодирование значений, только когда пользователь их запрашивает.</p><p>Вот упрощённый код первого этапа (без обработки краевых случаев):</p><p>Несколько важных деталей:</p><ul><li>Здесь используется TrailingZeroCount вместо PopCount, потому что нам важна позиция каждого найденного символа, а не только их количество</li><li>Одна и та же схема CompareEqual + MoveMask применяется для каждого управляющего символа, а затем маски объединяются через OR. Отдельные маски сохраняются, чтобы потом определить тип найденного символа</li><li>Мы не касаемся содержимого полей — если пользователю нужен только один столбец, незачем декодировать остальные</li></ul><h2>Обработка кавычек и экранирования</h2><p>Когда поле содержит кавычки, нужно выполнить декодирование вручную — убрать обрамляющие кавычки и заменить двойные кавычки на одинарные. Кроме того, C# внутри использует UTF-16 для всех строк (char — это 16-битная единица кода), поэтому при чтении UTF-8 приходится конвертировать.</p><p>Если кавычек в поле нет, используется быстрая встроенная конвертация Encoding.UTF8.GetChars. Но если кавычки есть, быстрее выполнить конвертацию и удаление кавычек за один проход:</p><p>Хитрость здесь в том, что 2- и 3-байтовые UTF-8-последовательности всегда дают одну единицу кода UTF-16, поэтому кодпоинт можно просто привести к char. Четырёхбайтовые символы (эмодзи и редкие иероглифы) требуют суррогатных пар.</p><p>Для UTF-16-строк (внутренний формат C#) парсер использует трюк с Avx2.PackUnsignedSaturate: два 256-битных вектора 16-битных символов «сжимаются» в один 256-битный вектор 8-битных байтов. ASCII-символы сохраняются как есть, а все не-ASCII насыщаются до 255 (или 0 для отрицательных при знаковой интерпретации). После перестановки блоков через Avx2.Permute4x64 можно применять ту же маску, что и для UTF-8.</p><h2>Бенчмарки — результаты</h2><p>Существующие бенчмарки CSV-парсеров на C# тестируют только один файл — по сути, они измеряют не парсинг CSV, а производительность на одном конкретном сценарии. Поэтому автор создал собственный набор тестов с разными типами данных:</p><ul><li><b>Реальные данные Reddit</b> — широкий датасет с разными типами данных и большим количеством текста (из Reddit ArcticShift)</li><li><b>Международный текст</b> — CSV с названиями предметов из Pokemon на множестве языков (кириллица, японский, корейский)</li><li><b>Финансовые данные</b> — числа с минимумом текста</li><li><b>Синтетические тесты</b> — числовые поля, короткие файлы (50 строк), файлы с обязательным экранированием, «стресс-тест» с массой кавычек</li></ul><p>Полные результаты опубликованы на <a href="https://bepis.io/csv-benchmark/">bepis.io/csv-benchmark</a>.</p><p>Итог: <b>FourLambda.Csv оказался самым быстрым в 41 из 70 тестов</b>. Не абсолютная победа, но убедительное лидерство.</p><p>Для сравнения, вот как менялась скорость подсчёта символов на файле 500 МБ по мере оптимизации:</p><h3>Почему FourLambda.Csv быстрее Sep</h3><p>До этого самым быстрым CSV-парсером на C# считалась библиотека <a href="https://github.com/nietras/Sep">Sep</a>. Вот несколько причин, почему FourLambda.Csv выигрывает:</p><p><b>UTF-8 без промежуточной конвертации.</b> Sep внутри конвертирует UTF-8 в UTF-16 даже если данные не нужны пользователю. FourLambda.Csv работает напрямую с UTF-8.</p><p><b>Давление на кэш.</b> L1-кэш процессора — около 32-64 КБ, и он хранит не только данные, но и исполняемый код. FourLambda.Csv весит ~16 КБ, а Sep — ~163 КБ. Разница в 10 раз означает, что Sep может вытеснять данные из L1-кэша, вызывая медленные обращения к памяти.</p><p><b>Переоптимизация.</b> Sep тщательно контролирует, как данные перемещаются между регистрами. Теоретически это быстрее, но на практике может мешать оптимизациям рантайма. Компилятор и процессор умеют оптимизировать код, если им не указывают точный способ выполнения. Чрезмерная микрооптимизация «связывает руки» и мешает автоматическим улучшениям в новых версиях .NET.</p><p><b>Зависимость от оборудования.</b> Результаты могут отличаться на разных машинах. Например, Sse.Prefetch0 (предзагрузка данных в L1) давала +10-20% на ноутбуке с DDR3, но почти нулевой эффект на десктопе с DDR5. На системах с AVX-512 и ARM у Sep есть аппаратные оптимизации, которых пока нет в FourLambda.Csv.</p><h2>Выводы</h2><p>История этого проекта — хороший пример того, как глубокое понимание низкоуровневых деталей (кодировки, устройство кэша, SIMD-инструкции) может привести к результатам, которые превосходят годы целенаправленных оптимизаций в существующих библиотеках.</p><p>Главные уроки:</p><ul><li>SIMD — не магия для избранных. В C# интринсики доступны из коробки и дают кратный прирост на задачах поиска и сравнения</li><li>UTF-8-совместимость ASCII — мощное свойство, которое можно эксплуатировать для быстрого парсинга</li><li>Минимализм побеждает сложность: меньше кода — меньше давления на кэш — быстрее выполнение</li><li>Не мешайте компилятору: иногда лучшая оптимизация — не оптимизировать вручную</li></ul><p>Библиотека доступна на GitHub: <a href="https://github.com/bbepis/FourLambda.Csv">bbepis/FourLambda.Csv</a>.</p><p><i>Перевод и адаптация статьи <a href="https://bepis.io/blog/turbo-csv-parser/">How I accidentally made the fastest C# CSV parser</a> с разрешения автора.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Полнотекстовый поиск на Go без Elasticsearch — гайд по Bleve</title>
      <link>https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve</link>
      <comments>https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve</guid>
      <description><![CDATA[<p>Bleve — встраиваемый полнотекстовый движок для Go. Разбираем кастомные маппинги, мультиязычные индексы, курсорную пагинацию и настройку Scorch для продакшена.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/polnotekstovyj-poisk-na-go-bez-elasticsearch---gajd-po-bleve">Полнотекстовый поиск на Go без Elasticsearch — гайд по Bleve</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:25:26 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда в проекте появляется задача полнотекстового поиска, первая мысль — поставить <a href="https://www.elastic.co/elasticsearch">Elasticsearch</a> или <a href="https://www.meilisearch.com/">Meilisearch</a>. Оба инструмента отлично справляются. Но что, если вы не хотите зависеть от внешнего сервиса — или вам нужен полный контроль над хранением и обработкой данных?</p><p>В Go для этого есть <a href="https://blevesearch.com/">Bleve</a> — файловая библиотека полнотекстового индексирования. Она умеет индексировать любые Go-структуры с разумными настройками по умолчанию, поддерживает встроенный язык запросов в стиле Google, работает с миллионами записей и не требует отдельного сервера.</p><p>В этой статье — практический гайд: от простого индекса до кастомных анализаторов, мультиязычного поиска, пагинации курсором и тонкой настройки производительности.</p><blockquote><b>Ключевые выводы</b><br /><br />- Bleve — встраиваемый полнотекстовый движок для Go: не нужен отдельный сервер, достаточно одной зависимости<br />- Кастомные маппинги позволяют настроить анализатор, стемминг и стоп-слова для каждого поля отдельно<br />- IndexAlias объединяет несколько индексов (например, по языкам) в один виртуальный — с единым интерфейсом поиска<br />- Курсорная пагинация через SearchAfter / SearchBefore работает стабильно даже при обновлении индекса между запросами<br />- Scorch-настройки (воркеры, размер сегментов, порог мёржа) критичны для производительности под нагрузкой</blockquote><h2>Создаём простой индекс</h2><p>Начнём с минимального примера. Два базовых действия — <b>индексирование</b> (сохранение документа для последующего поиска) и <b>запрос</b> (извлечение документов, отсортированных по релевантности).</p><p>Несколько важных деталей:</p><ul><li><b>bleve.New vs bleve.Open</b> — New создаёт новый индекс по указанному пути, Open открывает существующий. Паттерн из примера (сначала New, при ошибке — Open) — идиоматический способ обработки первого и повторных запусков.</li><li><b>NewIndexMapping()</b> — возвращает маппинг по умолчанию: текстовые поля токенизируются, приводятся к нижнему регистру и фильтруются через список стоп-слов английского языка.</li><li><b>Автоматическое обнаружение полей</b> — Bleve использует рефлексию для анализа структуры. Все экспортируемые поля автоматически токенизируются и становятся доступными для поиска.</li><li><b>Уникальные ID документов</b> — строковый идентификатор, передаваемый в Index, используется для обновлений и удалений. Повторный вызов Index с тем же ID заменяет документ.</li><li><b>SearchRequest.Fields</b> — по умолчанию Bleve возвращает только ID и релевантность. Укажите имена нужных полей или []string{"*"}, чтобы получить все.</li><li><b>hit.Score</b> — каждый результат содержит числовой показатель релевантности на основе BM25. Чем выше — тем точнее совпадение.</li></ul><h2>Кастомные маппинги полей</h2><p>Маппинг по умолчанию подходит для быстрого старта, но в реальных проектах нужен контроль над тем, как Bleve анализирует и хранит каждое поле. <b>Маппинг</b> определяет тип поля, анализатор для токенизации, необходимость хранения оригинального значения и включение в индекс.</p><p>Маппинги позволяют:</p><ul><li><b>Управлять токенизацией</b> — разбивать текст на термы по пробелам, языковым правилам, edge n-граммам и другим стратегиям</li><li><b>Фильтровать входные данные</b> — приводить к нижнему регистру, удалять HTML, применять стоп-слова и стемминг (чтобы «running» и «runs» находились по запросу «run»)</li><li><b>Исключать поля из индекса</b> — пропускать чувствительные или нерелевантные данные для экономии дискового пространства</li><li><b>Создавать кастомные анализаторы</b> — комбинировать любой токенизатор с произвольной цепочкой фильтров</li></ul><p>Пример ниже применяет стемминг английского языка к полям Text и Title, а поле с сырым HTML исключает из индексирования:</p><p>Результат buildIndexMapping() передаётся в bleve.New или bleve.NewUsing при создании индекса. Маппинги закрепляются за индексом на этапе создания и не могут быть изменены позже. Чтобы применить новый маппинг, нужно создать свежий индекс и переиндексировать все документы.</p><h2>Мультиязычный поиск</h2><p>Bleve умеет прозрачно работать с несколькими индексами одновременно через <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#IndexAlias">IndexAlias</a>. Алиас — это виртуальный индекс, который отправляет запрос во все реальные индексы и объединяет результаты в единый ранжированный список.</p><p>Это особенно полезно, когда у каждого языка свой индекс с собственным анализатором (английский стемминг, французские стоп-слова, кастомная токенизация), а искать нужно по всем сразу:</p><p>Алиасы также упрощают горячую замену (hot-swap). Когда нужно перестроить индекс (например, применить новый маппинг), можно собрать новый индекс в фоне, а затем атомарно подменить его вызовом alias.Swap(newIndexes, oldIndexes). Текущие запросы завершатся на старом индексе, новые сразу пойдут на свежий — без даунтайма.</p><h2>Пагинация с курсором</h2><p>У SearchRequest в Bleve есть поле From для офсетной пагинации: 0 для первой страницы, 20 для второй и так далее. Это работает, но имеет серьёзную проблему: Bleve вынужден оценивать и сортировать <i>все</i> совпавшие документы вплоть до From + Size на каждый запрос. Глубокие страницы становятся всё дороже по памяти и CPU.</p><p>Хуже того, если между двумя запросами в индекс были добавлены новые документы, смещение сдвигается — и пользователь видит дубликаты или пропуски.</p><p>Правильный подход — курсорная пагинация через <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#SearchRequest.SetSearchAfter">SearchAfter</a> и <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#SearchRequest.SetSearchBefore">SearchBefore</a>. Эти методы продолжают выдачу с известной позиции, а не пересканируют результаты с начала:</p><p>На что обратить внимание:</p><ul><li><b>Стабильная сортировка обязательна.</b> SearchAfter использует ключ сортировки последнего результата в качестве курсора. Если ключ нестабилен, курсор станет невалидным.</li><li><b>Sort — всегда []string.</b> Даже при сортировке по числовому полю Bleve сериализует ключ в строку. Читайте курсор из hit.Sort и передавайте напрямую в SetSearchAfter.</li><li><b>SearchBefore работает аналогично,</b> но в обратном направлении — полезно для кнопки «предыдущая страница».</li></ul><h2>Оптимизация производительности</h2><p>Настройки производительности Bleve не слишком хорошо документированы, но существенно влияют на работу под нагрузкой. Конфигурация передаётся как map[string]any в <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#NewUsing">NewUsing</a> или <a href="https://pkg.go.dev/github.com/blevesearch/bleve/v2#OpenUsing">OpenUsing</a> вместо обычных New / Open.</p><p>Эти параметры относятся к Scorch — дефолтному бэкенду хранения Bleve. Полный список доступных опций и значений по умолчанию можно найти в <a href="https://github.com/blevesearch/bleve/blob/master/index/scorch/persister.go#L67">исходном коде персистера</a>.</p><h3>Пакетная индексация</h3><p>Если нужно проиндексировать большой объём данных, используйте Batch вместо одиночных вызовов Index. Пакетная запись группирует операции в одну транзакцию, что значительно снижает нагрузку на диск:</p><p>Оптимальный размер пакета зависит от объёма документов и доступной памяти. Как правило, пакеты по 100-1000 документов дают хороший баланс между скоростью и потреблением ресурсов.</p><h2>Выводы</h2><p>Bleve — одна из недооценённых жемчужин экосистемы Go. Библиотека позволяет добавить полнотекстовый поиск в приложение без сложной инфраструктуры. Настройки по умолчанию дают рабочий результат за минуты, а кастомные маппинги, композитные запросы и тонкая настройка Scorch — инструменты для решения специфических задач оптимально.</p><p>Официальная документация местами неполна, но issues на GitHub и реальные open-source-проекты отлично её дополняют. Рабочий пример всех описанных концепций можно найти в <a href="https://github.com/asciimoo/hister/tree/master/server/indexer">пакете indexer</a> проекта Hister.</p><p><i>Адаптированный перевод статьи <a href="https://hister.org/posts/data-indexing-in-golang">Data Indexing in Golang</a> из блога Hister.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Что на самом деле копирует fork() — и почему ваш пул соединений ломается</title>
      <link>https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-</link>
      <comments>https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-</guid>
      <description><![CDATA[<p>fork() копирует память процесса, но разделяет файловые дескрипторы. Разбираем реальный инцидент с Django, Celery и psycopg: как один флаг сломал все воркеры.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/chto-na-samom-dele-kopiruet-fork-----i-pochemu-vaw-pul-soedinenij-">Что на самом деле копирует fork() — и почему ваш пул соединений ломается</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 15:24:45 GMT</pubDate>
      <content:encoded><![CDATA[<p>Все воркеры <a href="https://docs.celeryq.dev/en/stable/">Celery</a> одновременно перестали получать соединения с базой. Ни один. Ни ошибок подключения, ни таймаутов запросов — просто тишина. Метрики пула показывали норму. Инфраструктура была в порядке. Но каждая задача падала через 20 секунд с одним и тем же сообщением:</p><p>Проблема оказалась не в сети, не в базе и не в нагрузке. Проблема была в одном булевом флаге, который изменил момент открытия пула соединений — и превратил fork() из безопасной операции в мину замедленного действия.</p><blockquote><b>Ключевые выводы:</b><br />— fork() копирует память процесса (copy-on-write), но разделяет файловые дескрипторы с ядром<br />— TCP-сокеты после fork() указывают на один и тот же объект ядра — два процесса пишут в один поток<br />— threading.Lock ломается: futex-ключ привязан к физическому адресу памяти, который меняется при copy-on-write<br />— Фоновые потоки пула просто не существуют в дочернем процессе — fork() копирует только вызывающий поток<br />— Правило: никогда не держите открытые соединения перед fork()</blockquote><h2>Инцидент — что случилось</h2><p>За несколько дней до инцидента в конфигурации изменился один флаг. Нужно было, чтобы обработчики сигналов <a href="https://www.djangoproject.com/">Django</a> регистрировались внутри воркеров Celery. Существующая настройка пропускала регистрацию, если флаг не был установлен в true.</p><p>Флаг установили. Сигналы заработали. Фича уехала в прод.</p><p>QA был быстрым. Изменение выглядело изолированным: переключить флаг, убедиться, что сигналы регистрируются в Celery, готово. Никто не искал побочных эффектов, спрятанных в AppConfig.ready().</p><p>А потом тихо начало выполняться кое-что ещё. Несколько методов AppConfig.ready(), которые теперь стали активны, делали ORM-запросы при старте: создавали расписания периодических задач, проверяли записи crontab. Рутинные вещи. Ничего опасного на вид.</p><p>Но эти запросы открыли пул соединений с базой данных. В мастер-процессе. <b>До fork()</b>.</p><p>Модель конкурентности Celery по умолчанию — prefork. Не потоки, не async-воркеры. Celery использует fork() — системный вызов POSIX — для создания пула рабочих процессов. Эту деталь легко забыть, когда смотришь на код приложения. Но она становится критически важной, когда открываешь соединения с базой при старте.</p><p>В этом и была проблема.</p><h2>Что копирует fork()</h2><p>Большинство разработчиков знают поверхностный ответ: fork() создаёт дочерний процесс, который является копией родительского. Но слово «копия» скрывает важное различие, которое ядро проводит между двумя типами ресурсов.</p><h3>Память — copy-on-write</h3><p>Когда fork() выполняется, ядро не дублирует RAM немедленно. Вместо этого оно помечает таблицы страниц обоих процессов как read-only, указывая на одни и те же физические страницы. Фактическое копирование происходит только когда один из процессов пишет в страницу: ядро перехватывает page fault, выделяет новую физическую страницу, копирует содержимое и обновляет таблицу страниц. До этого момента оба процесса разделяют физическую память, не зная об этом.</p><p>Это эффективно. И именно поэтому Python-объекты, включая внутреннее состояние пула соединений, выглядят целыми в дочернем процессе. Дочерний процесс получает свою копию pool._pool, pool._lock, pool._sched. Байты на месте. Структура на месте.</p><p>Но некоторые из этих байтов указывают на ресурсы ядра. А ресурсы ядра не копируются.</p><h3>Файловые дескрипторы — общие</h3><p>TCP-сокет — это не Python-объект. Это объект ядра: struct file со счётчиком ссылок, за которым стоит struct sock с буферами отправки и получения, состоянием TCP, sequence numbers. Когда fork() выполняется, ядро вызывает dup_fd() для таблицы файловых дескрипторов родителя: каждый открытый fd дублируется в дочерний процесс, а счётчик ссылок на struct file увеличивается.</p><p>Оба процесса теперь держат fd=12. Оба указывают на один и тот же объект сокета в ядре. Один TCP-поток, два читателя и два писателя — без координации между ними.</p><p>Проводной протокол <a href="https://www.postgresql.org/">PostgreSQL</a> — stateful. Он ожидает последовательные пары запрос-ответ в одном потоке. Два процесса, чередующие байты в одном соединении, не создают два независимых разговора. Они создают мусор.</p><h3>Блокировки — сломанный futex</h3><p>threading.Lock построен на pthread_mutex_t, который внутри опирается на futex — механизм ядра Linux, использующий <i>физический адрес памяти</i> целого числа как ключ для очереди ожидания. После fork() copy-on-write может переместить страницу дочернего процесса на новый физический адрес при записи. Futex-ключ дочернего процесса расходится с родительским. futex_wake из одного процесса не будит никого в другом.</p><p>POSIX явно говорит об этом: поведение мьютекса после fork() не определено, если он не был создан с атрибутом process-shared. Python-овский Lock этот атрибут не использует.</p><h3>Фоновые потоки — просто не существуют</h3><p>fork() дублирует только вызывающий поток. Внутренний планировщик пула — отвечающий за поддержание минимального числа соединений, проверки здоровья, уведомление ожидающих — копируется как Python-объект, но не имеет соответствующего потока ОС. Его TID не существует. Любой путь кода, который зависит от его ожидания или сигнализации, заблокируется навсегда.</p><p>Дочерние процессы унаследовали пул, который выглядел целым, но был мёртв. Они вызывали pool.getconn(), пытались захватить сломанный лок, ждали 20 секунд notify, который никогда не придёт, и получали таймаут.</p><h2>Почему пул соединений ломается после fork()</h2><p>Пул соединений <a href="https://www.psycopg.org/psycopg3/docs/">psycopg</a> (ConnectionPool) хранит три вида ресурсов. Каждый ломается по-своему после fork().</p><p><b>TCP-сокеты</b> разделяются на уровне ядра. Любая попытка использовать их из двух процессов одновременно разрушает поток протокола. На практике дочерние процессы до этого даже не доходили.</p><p><b>threading.Lock</b> опирается на futex, привязанный к физическому адресу. После copy-on-write ключ дрейфует. futex_wake будит очередь по старому адресу — никого в дочернем процессе там нет.</p><p><b>Фоновые потоки</b> не существуют в дочернем процессе. Планировщик пула — Python-объект без ОС-потока. Код, зависящий от его notify, блокируется навечно.</p><p>Вот почему раньше всё работало:</p><p>До изменения флага AppConfig.ready() пропускал регистрацию периодических задач. ORM не вызывался при старте. Нет запросов — нет пула. Нет пула — нечего наследовать. Каждый дочерний процесс начинал с чистого состояния и лениво создавал собственный пул при первом обращении к базе: собственные сокеты, собственный лок, собственный фоновый поток.</p><h2>Решение — чистый fork без наследства</h2><p>Два хука сигналов Celery.</p><p>worker_before_create_process срабатывает в родительском процессе после ready(), перед каждым fork(). Он закрывает все соединения с базой по всем алиасам и уничтожает пулы соединений. К моменту выполнения fork() нет открытых TCP-сокетов, нет локов, нет фоновых потоков для наследования.</p><p>worker_process_init срабатывает в каждом дочернем процессе после fork() как второй уровень защиты — defense-in-depth, на случай если что-то было пропущено.</p><p>После обоих хуков каждый дочерний процесс пуст. Первый ORM-запрос создаёт свежий пул, который целиком принадлежит этому процессу.</p><p>Принцип простой: никогда не держите открытые соединения перед fork() — или явно закройте и уничтожьте их до форка. Это правило применимо к любому пулу соединений на основе TCP: <a href="https://www.psycopg.org/psycopg3/docs/">psycopg</a>, <a href="https://www.sqlalchemy.org/">SQLAlchemy</a>, redis-py. Технология не важна. Важно поведение ядра.</p><h2>Диаграммы — до и после</h2><h3>До изменения флага: чистый fork, каждый воркер создаёт свой пул</h3><h3>После изменения флага: пул открыт в мастере, fork() распространяет повреждение</h3><h3>После исправления: пул уничтожен перед fork(), каждый воркер стартует чисто</h3><h2>Выводы</h2><p>Настоящий урок — не в конкретном фиксе. Он в понимании того, почему пул сломался. Что fork() на самом деле копирует. Что он разделяет. Почему лок, который выглядит целым, может заблокировать дочерний процесс. Почему поток, существующий как Python-объект, может полностью отсутствовать в ОС.</p><p>Правила, которые стоит запомнить:</p><ul><li>Никогда не открывайте пул соединений до fork() — или явно уничтожьте его перед форком</li><li>fork() копирует память (лениво), но разделяет файловые дескрипторы на уровне ядра</li><li>threading.Lock после fork() — undefined behavior: futex привязан к физическому адресу, который дрейфует при copy-on-write</li><li>Фоновые потоки не дублируются — fork() копирует только вызывающий поток</li><li>Это правило касается любого пула TCP-соединений: psycopg, SQLAlchemy, redis-py — поведение ядра одинаково</li></ul><p>Невидимая часть — это взаимодействие между последовательностью запуска Django и моделью процессов Celery. Две системы, каждая из которых хорошо понятна по отдельности, делают неожиданное на стыке.</p><p><i>Адаптированный перевод статьи <a href="https://tech.daniellbastos.com.br/posts/what-fork-actually-copies/">What fork() Actually Copies</a> Даниэля Бастоса (Daniel Bastos).</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Ваш debounce вас обманывает — и вот почему</title>
      <link>https://tproger.ru/translations/vaw-debounce-vas-obmanyvaet---i-vot-pochemu</link>
      <comments>https://tproger.ru/translations/vaw-debounce-vas-obmanyvaet---i-vot-pochemu?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/vaw-debounce-vas-obmanyvaet---i-vot-pochemu</guid>
      <description><![CDATA[<p>Debounce снижает частоту вызовов, но не контролирует сетевые запросы. Разбираем race conditions и ошибки, исправляем через AbortController и retry.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/vaw-debounce-vas-obmanyvaet---i-vot-pochemu">Ваш debounce вас обманывает — и вот почему</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 14:02:07 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://www.geeksforgeeks.org/javascript/debouncing-in-javascript/">Debounce</a> — один из тех паттернов, которые фронтенд-разработчик узнаёт в самом начале карьеры и использует всю оставшуюся жизнь.</p><p>По сути debounce делает одну простую вещь: собирает серию вызовов и превращает их в один вызов после паузы. Отлично подходит для «шумных» UI-событий.</p><p>Самый типичный пример — автодополнение в поиске. Но тот же паттерн работает для обработки resize, scroll, live-валидации, фильтров и хуков аналитики.</p><p>Классическая реализация выглядит так:</p><p>Выглядит дисциплинированно. Ощущается эффективно. Быстро доезжает до прода.</p><p>И вот тут начинается обман.</p><p>Проблема не в самом debounce. Проблема в связке «debounce + fetch», когда в уравнение входит реальная сеть.</p><p>Debounce создаёт ощущение, что запросы «под контролем». Но он не контролирует жизненный цикл запроса: порядок ответов, отмену устаревших запросов, поведение при ошибках.</p><p>Именно поэтому в продакшене debounce «врёт»: UI выглядит плавно, а сетевой слой по-прежнему хрупкий.</p><p>В этой статье мы оставим debounce для того, в чём он хорош (сглаживание UI), и укрепим сетевой слой отменой запросов, повторными попытками и корректной обработкой ошибок.</p><blockquote><b>Ключевые выводы:</b><br />— Debounce — это паттерн UI, а не паттерн работы с сетью<br />— Он гарантирует только одно: «я не буду вызывать функцию слишком часто»<br />— Порядок ответов, отмена устаревших запросов и обработка ошибок — это то, что вам придётся решать отдельно<br />— AbortController отменяет устаревшие запросы на уровне сети<br />— Повторные попытки с экспоненциальной задержкой спасают от транзиентных ошибок сервера</blockquote><h2>Проблема 1 — гонка запросов (race conditions)</h2><p>На локальной машине всё летает. Но в продакшене сеть непредсказуема: запросы могут приходить с разной задержкой, и нет никакой гарантии, что ответы придут в том же порядке, в каком были отправлены.</p><p>Представьте: пользователь печатает 12345678. Debounce пропускает запросы для 1234567 и 12345678. Ответ на 1234567 задерживается на сервере и приходит <i>после</i> ответа на 12345678. UI обновляется последним пришедшим ответом — и показывает устаревшие данные.</p><p>Это классическая гонка запросов, и debounce сам по себе её не предотвращает.</p><h3>Решение — AbortController</h3><p>Нам нужно гарантировать, что обрабатывается только ответ на последний запрос, а все предыдущие — отменяются. <a href="https://developer.mozilla.org/en-US/docs/Web/API/AbortController">AbortController</a> — это браузерный API, который позволяет отменять fetch-запросы. Создаём контроллер, передаём его signal в fetch, и вызываем abort(), когда нужно отменить запрос.</p><p>Что изменилось:</p><ul><li>Перед каждым запросом мы отменяем предыдущий через abort() и создаём новый контроллер</li><li>В блоке catch проверяем, является ли ошибка AbortError — это ожидаемое поведение, а не реальный сбой</li></ul><p>Результат: в обычном потоке только последний запрос из серии нажатий доходит до конца. Предыдущие отменяются на уровне сети, а не просто игнорируются после получения ответа.</p><h2>Проблема 2 — сетевые ошибки</h2><p>Сеть непредсказуема не только по задержкам, но и по надёжности. Иногда запрос, который мог бы пройти при повторной попытке, просто падает. Причины: кратковременная перегрузка сервера, пики нагрузки, таймауты базы данных.</p><h3>fetch не бросает исключение при HTTP-ошибках</h3><p>Это одна из главных ловушек нативного fetch: он отклоняет промис только при сетевых сбоях (нет соединения). Коды 4xx и 5xx — это «успешные» ответы с точки зрения fetch. Если сервер вернёт 500, ваш код радостно вызовет response.json() и получит undefined вместо данных.</p><p>Исправляем проверкой response.ok:</p><p>Теперь 500-я ошибка выбрасывает исключение до того, как мы пытаемся разобрать тело ответа. Блок catch обработает её корректно.</p><h3>Повторные попытки с экспоненциальной задержкой</h3><p>Но простая проверка — это только начало. В реальном приложении стоит добавить автоматические повторные попытки для транзиентных ошибок. Если запрос упал из-за временной проблемы, лучше попробовать ещё раз с нарастающей задержкой, чем сразу показывать ошибку пользователю.</p><p>Писать логику повторов вручную — это циклы, счётчики попыток, тайминги, и всё это должно корректно работать с отменой. Нетривиально и неинтересно. Воспользуемся библиотекой <a href="https://github.com/nickersoft/fetchkit">@fetchkit/ffetch</a> — это тонкая обёртка над fetch, которая решает именно эту задачу.</p><p>Есть и альтернативы: <a href="https://github.com/sindresorhus/ky">ky</a>, <a href="https://github.com/axios/axios">axios</a> или собственная обёртка. ffetch выбран за совместимый с fetch API и корректную работу с AbortController при повторных попытках.</p><p>Что нам это даёт:</p><ul><li>retries: 3 — при 500-й ошибке библиотека повторяет запрос до 3 раз</li><li>shouldRetry — повторяем только при 5xx; всё остальное (сетевая ошибка, отмена) пробрасывается сразу</li><li>throwOnHttpError: true — автоматически бросает исключение на HTTP-ошибки, не нужна ручная проверка response.ok</li><li>Задержка между повторами учитывает AbortController — если abort() вызван во время ожидания, повтор немедленно прекращается</li></ul><p>Последний пункт особенно важен. Без этого отмена запроса в середине серии повторов убила бы текущий fetch, но оставила бы таймер — и следующая попытка сразу бы упала с AbortError.</p><h2>Полное решение</h2><p>Собираем всё вместе: debounce для UI-сглаживания, AbortController для отмены устаревших запросов, ffetch для повторных попыток и автоматической обработки HTTP-ошибок.</p><p>Каждый слой отвечает за своё: debounce снижает частоту вызовов, AbortController гарантирует, что обрабатывается только актуальный запрос, а ffetch добавляет устойчивость к транзиентным сбоям.</p><h2>Частые вопросы</h2><h3>Зачем AbortController, если debounce и так снижает количество запросов?</h3><p>Debounce снижает <i>частоту</i> вызовов, но не контролирует, что происходит с уже отправленными запросами. Если два запроса ушли один за другим, более ранний может вернуться позже — и перезаписать актуальные данные. AbortController отменяет устаревший запрос на уровне сети, а не просто игнорирует ответ.</p><h3>Можно ли обойтись без сторонней библиотеки для повторов?</h3><p>Да, можно написать retry-логику вручную. Но это циклы, счётчики, экспоненциальная задержка и корректная обработка отмены. В продакшен-коде проще использовать готовое решение — ffetch, ky или axios — чтобы не изобретать велосипед и не допускать ошибок в edge-кейсах.</p><h3>Работает ли этот подход с React / Vue / Angular?</h3><p>Да. AbortController и retry-логика — это чистый JavaScript, независимый от фреймворка. В React, например, AbortController часто используется в useEffect для отмены запросов при размонтировании компонента. Принцип тот же: debounce для UI, отмена и повторы для сетевого слоя.</p><h2>Выводы</h2><p>Debounce — не проблема. Проблема — считать его полным решением для управления сетевыми запросами, когда он контролирует только одно измерение: частоту вызовов.</p><p>Debounce — это паттерн UI, а не паттерн работы с сетью. Чтобы построить надёжное приложение, нужно дополнить его управлением жизненным циклом запросов:</p><ul><li>Отмена устаревших запросов через AbortController</li><li>Повторные попытки с экспоненциальной задержкой для транзиентных сбоев</li><li>Корректная обработка HTTP-ошибок (проверка response.ok или автоматический проброс через библиотеку)</li></ul><p>Тогда UI будет не только отзывчивым, но и точным — даже при непредсказуемых сетевых условиях.</p><p><i>Адаптированный перевод статьи <a href="https://blog.gaborkoos.com/posts/2026-03-28-Your-Debounce-Is-Lying-to-You/">Your Debounce Is Lying to You</a> Габора Кооша (Gabor Koos).</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Почему ИИ-сгенерированный код создаёт долг понимания</title>
      <link>https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya</link>
      <comments>https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya</guid>
      <description><![CDATA[<p>Адди Османи объясняет, почему ИИ-инструменты создают разрыв между объёмом кода и его пониманием. Как тесты и спецификации не решают проблему и что делать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/pochemu-ai-generirovannyj-kod-sozdayot-dolg-ponimaniya">Почему ИИ-сгенерированный код создаёт долг понимания</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 14:00:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>Команда работает быстрее, чем когда-либо. Метрики скорости бьют рекорды. Код-ревью проходят гладко, тесты зелёные, PR-ы сливаются один за другим. Но за этим блеском скрывается расход, который ни одна метрика не фиксирует: разрыв между тем, сколько кода существует в системе, и тем, сколько из него хоть кто-то по-настоящему понимает. <a href="https://addyosmani.com/blog/comprehension-debt/">Адди Османи</a> — инженерный лидер <a href="https://www.google.com/chrome/">Google Chrome</a>, автор книги <a href="https://www.patterns.dev/posts/classic-design-patterns">Learning JavaScript Design Patterns</a> — называет этот расход <b>долгом понимания</b> (comprehension debt). И предупреждает: проценты по нему растут быстрее, чем по техническому долгу.</p><p><b>Долг понимания</b> — это растущий разрыв между объёмом кода в системе и долей этого кода, которую живые люди действительно понимают. В отличие от технического долга, который заявляет о себе трением — медленными сборками, запутанными зависимостями, ощущением ужаса при касании «того самого» модуля — долг понимания порождает ложную уверенность. Кодовая база выглядит чистой. Тесты зелёные. Расплата приходит тихо, обычно в самый неподходящий момент.</p><p><a href="https://margaretstorey.com/blog/2026/02/09/cognitive-debt/">Маргарет-Энн Стори</a> описывает студенческую команду, которая наткнулась на стену на седьмой неделе: они больше не могли вносить простые изменения, не ломая что-нибудь неожиданное. Проблемой был не грязный код. Проблемой было то, что никто в команде не мог объяснить, почему были приняты те или иные проектные решения и как разные части системы должны работать вместе. Теория системы испарилась.</p><p>Недавнее исследование <a href="https://www.anthropic.com/research/AI-assistance-coding-skills">Anthropic</a> подтверждает это количественно. В рандомизированном контролируемом эксперименте с 52 инженерами участники, использовавшие ИИ-ассистента, завершали задачу за то же время, что и контрольная группа, — но на последующем тесте на понимание кода набрали на 17% меньше баллов (50% против 67%). Наибольшее падение — в навыках отладки. Пассивное делегирование («просто сделай это») разрушает формирование навыков значительно сильнее, чем активное использование ИИ для вопросов и изучения компромиссов.</p><blockquote><b>Ключевые выводы</b><br /><br />- Долг понимания — это не технический долг. Технический долг виден; долг понимания порождает ложную уверенность.<br />- ИИ генерирует код быстрее, чем человек способен его проверить. Прежний фильтр качества стал узким местом пропускной способности.<br />- Тесты необходимы, но недостаточны: нельзя написать тест на поведение, о котором никто не подумал.<br />- Спецификации тоже не спасают: между спецификацией и работающим кодом лежат сотни неявных решений.<br />- Ни одна текущая метрика не отражает этот долг. Velocity, DORA, покрытие кода — всё зелёное, пока понимание тихо выгорает.<br />- Удешевление генерации кода не удешевляет понимание. Работа по пониманию — это и есть работа инженера.</blockquote><h2>Асимметрия скорости — ИИ генерирует быстрее, чем человек проверяет</h2><p>ИИ генерирует код значительно быстрее, чем люди способны его оценить. Звучит очевидно, но последствия легко недооценить.</p><p>Когда разработчик в команде пишет код, код-ревью всегда было узким местом — но <b>продуктивным и обучающим</b>. Чтение пулл-реквеста вынуждает вникать. Оно вскрывает скрытые предположения, ловит проектные решения, конфликтующие с архитектурой полугодовой давности, и распределяет знание о том, что кодовая база реально делает, между людьми, ответственными за её поддержку.</p><p>ИИ-сгенерированный код ломает этот цикл обратной связи. Объём слишком велик. Результат синтаксически чистый, хорошо отформатированный, внешне корректный — именно те сигналы, которые исторически вызывали уверенность при мёрже. Но поверхностная корректность — не системная корректность. Кодовая база выглядит здоровой, а понимание тихо выгорает под ней.</p><p>И инверсия резче, чем кажется. Когда код было дорого производить, сеньоры могли ревьюить быстрее, чем джуниоры писали. <b>ИИ переворачивает это: джуниор теперь генерирует код быстрее, чем сеньор способен его критически проверить.</b> Ограничивающий фактор, который делал ревью осмысленным, исчез. То, что было фильтром качества, стало проблемой пропускной способности.</p><h2>Тесты — необходимы, но недостаточны</h2><p>Инстинктивная реакция — налечь на детерминированную верификацию: юнит-тесты, интеграционные тесты, статический анализ, линтеры, форматтеры. Автоматизировать выход из узкого места ревью. Пусть машины проверяют машины.</p><p>Это помогает. Но у этого подхода есть жёсткий потолок.</p><p>Набор тестов, способный покрыть всё наблюдаемое поведение, во многих случаях окажется сложнее, чем код, который он валидирует. А сложность, о которой невозможно рассуждать, не обеспечивает безопасность. И за этим стоит более фундаментальная проблема: <b>нельзя написать тест на поведение, о котором вы не подумали</b>.</p><p>Никто не пишет тест, утверждающий, что перетаскиваемые элементы не должны становиться полностью прозрачными. Конечно, не пишет — такая возможность никому не приходила в голову. Это именно тот класс сбоев, который проскальзывает — не потому, что тесты написаны плохо, а потому, что никто не подумал туда посмотреть.</p><p>Есть и конкретный сценарий провала, который стоит назвать. <b>Когда ИИ меняет поведение реализации и обновляет сотни тест-кейсов под новое поведение</b>, вопрос сдвигается с «правильный ли этот код?» на «были ли все эти изменения тестов необходимыми, и достаточно ли у меня покрытия, чтобы поймать то, о чём я не думаю?» Тесты не могут ответить на этот вопрос. Только понимание может.</p><p>Данные это подтверждают. Разработчики, делегирующие ИИ генерацию кода, набирают менее 40% на тестах понимания. Разработчики, использующие ИИ для концептуальных вопросов — исследования компромиссов, выяснения «почему» — набирают выше 65%. Инструмент не разрушает понимание. Его разрушает то, <i>как</i> вы этот инструмент используете.</p><h2>Спецификации — тоже не спасут</h2><p>Популярное предложение: напишите подробную спецификацию на естественном языке. Включите её в PR. Ревьюьте спецификацию, а не код. Доверьтесь тому, что ИИ точно перевёл намерение в реализацию.</p><p>Это привлекательно ровно так же, как когда-то был привлекателен Waterfall. Строго определите проблему, затем исполните. Чистое разделение ответственности.</p><p>Проблема в том, что перевод спецификации в работающий код включает огромное число неявных решений — крайние случаи, структуры данных, обработка ошибок, компромиссы производительности, паттерны взаимодействия — которые ни одна спецификация никогда полностью не охватывает. <b>Два инженера, реализующие одну и ту же спецификацию, создадут системы со множеством наблюдаемых поведенческих различий.</b> Ни одна реализация не будет неправильной. Они просто будут разными. И многие из этих различий в итоге будут важны для пользователей способами, которых никто не предвидел.</p><p>Есть ещё одно наблюдение: спецификация, достаточно детальная, чтобы полностью описать программу, — это, по сути, и есть программа, только написанная на неисполняемом языке. Организационные затраты на написание спецификаций, достаточно подробных для замены ревью, вполне могут превысить выигрыш в продуктивности от использования ИИ. И вы всё ещё не проревьюили то, что было реально произведено.</p><p>Глубинная проблема в том, что «правильной» спецификации часто не существует. Требования возникают в процессе создания. Крайние случаи обнаруживаются при использовании. Предположение о том, что нетривиальную систему можно полностью описать до её создания, проверялось многократно и было признано несостоятельным. ИИ этого не меняет — он лишь добавляет новый слой неявных решений, принятых без человеческого обдумывания.</p><h2>Учитесь у истории</h2><p>Десятилетия управления качеством ПО в распределённых командах с разным контекстом и пропускной способностью коммуникации выработали реальные, проверенные практики. Они не испаряются оттого, что участник команды теперь — модель.</p><p><b>Что меняется с ИИ:</b> стоимость (резко ниже), скорость (резко выше), управленческие накладные расходы на межличностное взаимодействие (практически ноль). <b>Что не меняется:</b> потребность в человеке с глубоким контекстом системы, который поддерживает связное понимание того, что кодовая база действительно делает и почему.</p><p>Это некомфортное перераспределение, к которому вынуждает долг понимания.</p><p>По мере роста ИИ-объёмов инженер, который по-настоящему понимает систему, становится <b>более ценным, а не менее</b>. Способность посмотреть на дифф и мгновенно понять, какие поведения несут нагрузку. Помнить, почему то архитектурное решение было принято под давлением восемь месяцев назад. Отличить безопасный рефакторинг от того, который тихо сдвигает нечто, от чего зависят пользователи. Этот навык становится дефицитным ресурсом, от которого зависит вся система.</p><h2>Проблема измерения — метрики не видят этот долг</h2><p><b>Долг понимания так опасен именно потому, что ничто в текущей системе измерений его не отражает.</b></p><p>Метрики скорости (velocity) выглядят безупречно. DORA-метрики держатся стабильно. Количество PR растёт. Покрытие кода тестами — зелёное.</p><p>Комиссии по оценке производительности видят рост velocity. Они не могут видеть дефицит понимания, потому что ни один артефакт измерения результатов не захватывает это измерение. Система стимулов оптимизирует правильно то, что она измеряет. Но то, что она измеряет, больше не отражает то, что имеет значение.</p><p>Именно поэтому долг понимания коварнее технического долга. Технический долг — обычно осознанный компромисс: вы выбрали короткий путь, примерно знаете, где он лежит, можете запланировать расплату. Долг понимания накапливается невидимо, часто без осознанного решения кого-либо допустить его. Это совокупный результат сотен ревью, где код выглядел нормально, тесты проходили, а в очереди ждал следующий PR.</p><p>Организационное допущение «отревьюенный код = понятый код» больше не работает. Инженеры утверждали код, который они не до конца понимали, — и этот код теперь несёт неявное одобрение. Ответственность распределилась, а заметить это не успел никто.</p><h2>Регуляторы придут быстрее, чем вы думаете</h2><p>Каждая индустрия, которая двигалась слишком быстро, в итоге привлекала регулирование. Технологический сектор был необычно защищён от этой динамики — отчасти потому, что сбои в ПО часто обратимы, отчасти потому, что индустрия двигалась быстрее, чем регуляторы успевали реагировать.</p><p>Это окно закрывается. Когда ИИ-сгенерированный код работает в системах здравоохранения, финансовой инфраструктуре и государственных сервисах, формулировка «ИИ написал это, а мы не полностью проверили» не выдержит критики в пост-инцидентном отчёте, когда на кону жизни или значительные активы.</p><p>Команды, которые выстраивают дисциплину понимания сейчас — рассматривая настоящее понимание, а не просто прохождение тестов, как обязательное условие — окажутся в лучшем положении, когда этот момент наступит, чем команды, оптимизировавшие исключительно скорость мёржа.</p><h2>Что на самом деле требует долг понимания</h2><p>Правильный вопрос сейчас — не «как генерировать больше кода?», а «как понимать больше из того, что мы отгружаем?» — чтобы пользователи получали стабильно качественный продукт.</p><p>Этот сдвиг фокуса имеет практические следствия:</p><ul><li>Будьте безжалостно явными в том, что изменение <i>должно делать</i>, прежде чем оно будет написано.</li><li>Рассматривайте верификацию не как послесловие, а как структурное ограничение.</li><li>Поддерживайте ментальную модель системного уровня, которая позволяет ловить ошибки ИИ на уровне архитектуры, а не построчно.</li><li>Будьте честны относительно разницы между «тесты прошли» и «я понимаю, что это делает и почему».</li></ul><p><b>Удешевление генерации кода не удешевляет понимание. Работа по пониманию — это и есть работа инженера.</b></p><p>ИИ берёт на себя перевод. Но кто-то по-прежнему должен понимать, что было произведено, почему именно так и были ли эти неявные решения правильными — иначе вы просто откладываете счёт, который в конце концов придёт с полной суммой.</p><p>Вы заплатите за понимание рано или поздно. Проценты по этому долгу растут быстро.</p><h2>Частые вопросы</h2><h3>Чем долг понимания отличается от технического долга?</h3><p>Технический долг — обычно осознанный компромисс. Вы знаете, где лежит короткий путь, и можете запланировать рефакторинг. Долг понимания накапливается невидимо: код выглядит чистым, тесты зелёные, но никто в команде не может объяснить, почему система работает именно так. Расплата приходит внезапно — когда простое изменение ломает всё каскадом.</p><h3>Значит ли это, что ИИ-инструменты для кодинга вредны?</h3><p>Нет. Исследование Anthropic показало, что инструмент не разрушает понимание — его разрушает способ использования. Пассивное делегирование («сделай за меня») снижает понимание, а активное использование для концептуальных вопросов и исследования компромиссов — нет. Ключ в том, чтобы использовать ИИ как партнёра для мышления, а не как замену мышлению.</p><h3>Как измерить долг понимания в команде?</h3><p>Прямых метрик пока нет — в этом и проблема. Косвенные индикаторы: частота каскадных сбоев от «простых» изменений, время на onboarding новых разработчиков, способность членов команды объяснить архитектурные решения без обращения к коду. Если никто не может ответить «почему это сделано именно так» — долг уже высок.</p><h3>Что делать прямо сейчас?</h3><p>Три вещи. Во-первых, требуйте от каждого изменения явного описания того, <i>что</i> оно должно делать, до написания кода. Во-вторых, поддерживайте ментальную модель архитектуры — это позволяет ловить ошибки на системном уровне, а не построчно. В-третьих, различайте «тесты прошли» и «я понимаю, что это делает». Первое — автоматизация. Второе — инженерная работа.</p><h2>Выводы</h2><p>Долг понимания — это не повод отказываться от ИИ-инструментов. Это повод осознанно управлять ценой, которую мы платим за скорость. Адди Османи формулирует это точно: код стал дешёвым в производстве, но понимание дешёвым не стало. И чем больше мы генерируем, тем дороже обходится разрыв.</p><p>Оригинал статьи: <a href="https://addyosmani.com/blog/comprehension-debt/">Comprehension Debt — the hidden cost of AI generated code</a> (Addy Osmani, март 2026).</p>]]></content:encoded>
    </item>
    <item>
      <title>Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</title>
      <link>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</link>
      <comments>https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto</guid>
      <description><![CDATA[<p>30 лучших уроков из 113 по масштабированию Django-монолита до 1 000 000 строк: архитектура, тестирование, мониторинг, безопасность, деплой и карьерный рост.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/maswtabirovanie-monolita-do-1-mln-strok--uroki-ot-timlida-do-cto">Масштабирование монолита до 1 млн строк: уроки от тимлида до CTO</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Что можно узнать из монолита на миллион строк кода? Джек Кинселла проработал 7 лет техлидом и CTO в <a href="https://www.morphmarket.com/">MorphMarket</a> — маркетплейсе на стеке <a href="https://www.djangoproject.com/">Django</a> / <a href="https://react.dev/">React</a> (TS) / React Native с командой из 20 человек. За это время кодовая база выросла до 1 000 000 строк, а Джек прошёл путь от первого инженера до технического директора.</p><p>Он опубликовал 113 уроков, которые вынес из этого опыта. Мы отобрали самые ценные, сгруппировали по темам и адаптировали для русскоязычной аудитории. Каждый урок — практический: его можно взять и применить в своём проекте уже сегодня.</p><blockquote><b>Ключевые выводы</b><br /><br />— Кэширование — последняя мера. Сначала исправьте модель данных, индексы и N+1-запросы.<br />— E2E-тесты на happy path дают в 10 раз больше отдачи, чем юнит-тесты на отдельные методы.<br />— Доведите Sentry до inbox zero — это единственный самый мощный шаг для качества продукта.<br />— Не обвиняйте конкретного разработчика за баг в проде: это всегда каскад из 3+ ошибок системы.<br />— Деплой за 2 минуты вместо 28 — достижимо, и это меняет культуру принятия решений.</blockquote><h2>Про архитектуру и масштабирование</h2><h3>Используйте сервисные объекты вместо раздувания моделей</h3><p>В фреймворках, где модели строятся вокруг существительных (Product, User, Auction), логика неизбежно скапливается в этих классах. Со временем каждый из них превращается в монстра на тысячи строк. Решение — выносить процессы в отдельные сервисные объекты: PlaceAuctionBidService, ProductBillingService. Суть — перестать мыслить существительными и начать мыслить глаголами.</p><h3>Одна и та же вещь, реализованная дважды — провал технического лидерства</h3><p>Если в кодовой базе есть два разных клиента к одному платёжному провайдеру, три файла с функциями для работы с датами или дублирующиеся UI-компоненты — это симптом отсутствия ответственного за архитектуру. Создавайте очевидные, легко находимые места для общего кода (common/datetime.py, common/geo.py) и отмечайте переиспользуемый код на ревью.</p><h3>Полиморфные связи в БД — почти всегда ошибка</h3><p>ORM-код для полиморфных отношений становится крайне запутанным, и вы теряете защиту через foreign key constraints. Проще добавить nullable FK для каждой связанной модели и написать абстракции на уровне запросов.</p><h3>Дефолтные фильтры и сортировки в ORM — всегда ошибка</h3><p>Скрытые дефолты вроде order_by('-id') или filter(is_deleted=False) становятся миной замедленного действия, когда в команде 20+ человек. Новый разработчик не знает про неявное поведение — и получает баг. А скрытый ORDER BY портит аналитические запросы, потому что ORM добавляет ключ сортировки в SELECT.</p><h3>Вся инфраструктура — в коде</h3><p>Когда Джек пришёл в MorphMarket, все 27 крон-задач были настроены вручную на продакшн-сервере. Большинство отсутствовали на staging. Перенос в код (а затем конфигурация AWS/Heroku/CloudFlare/Sentry через <a href="https://www.terraform.io/">Terraform</a>) сделал все окружения похожими друг на друга — и, что важно, сделал инфраструктуру доступной для чтения AI-агентам.</p><h2>Про тестирование</h2><h3>E2E &gt; интеграционные &gt; юнит-тесты</h3><p>Пользователям важно, чтобы система работала целиком. Один E2E-тест на happy path каждой ключевой фичи (<i>регистрация, покупка, создание листинга</i>) даёт на порядок больше уверенности, чем десятки юнит-тестов. Детали дорабатывайте интеграционными и юнит-тестами — они быстрее и проще в поддержке.</p><h3>Автоматические тесты для шаблонных фич</h3><p>В MorphMarket написали систему, которая автоматически тестирует около 300 админ-страниц: парсит имя страницы, находит фабрику, создаёт запись в БД и проверяет, что страница открывается. Тот же подход можно применить к любому CRUD-эндпоинту без выделенного теста.</p><h3>Изолируйте тесты полностью</h3><p>Хрупкость тестов убивает мотивацию команды. Основные ловушки:</p><ul><li>Всё, что связано со временем — замораживайте или мокайте</li><li>Фиксируйте seed для всех источников случайности — в каждой библиотеке</li><li>Запретите реальные HTTP-запросы в не-E2E тестах (используйте VCR-подобные библиотеки)</li><li>Отдельный Redis для тестов с автоочисткой перед каждым тестом</li><li>Тестовая среда не должна читать ни одной переменной из вашего .env</li></ul><h3>Мокайте только внешние объекты</h3><p>Мок — полезный инструмент, но только для внешних зависимостей. Мокать внутренние методы тестируемого класса — антипаттерн. Если можно переписать тест без мока без потери скорости — так и сделайте.</p><h3>CI должен проверять не только ошибки, но и раздражающие мелочи</h3><p>В MorphMarket CI также ловит новые warning-и в тестах, рассинхронизацию SDK с бэкендом и забытые миграции в Django. Чем раньше замечена мелочь, тем дешевле её исправить.</p><h2>Про отладку и мониторинг</h2><h3>Sentry до inbox zero — самый мощный шаг к качеству</h3><p>Когда Джек пришёл в компанию, Sentry показывал 1 000 ошибок в час. После агрессивной фильтрации (игнор старых браузеров, расширений, пометка JavaScript-ошибок уникальным идентификатором) и починки реальных багов — меньше 1 ошибки в час. Результат: каждая новая ошибка стала <i>событием</i>, на которое команда реагирует немедленно.</p><h3>Dead Man Switch — мониторьте то, что должно происходить</h3><p>Исключения шумят и легко ловятся. А вот отсутствие события — нет. Если крон-система перестала работать или бэкапы БД не снимаются — вы узнаете об этом только через heartbeat-мониторы. Настройте их.</p><h3>Username в каждой строке лога</h3><p>По умолчанию в логах — только IP-адрес. Но IP ротируется, создаёт лишнее перенаправление при дебаге и не показывает картину при использовании нескольких устройств. Поместите текущий запрос в thread-local и добавьте username в каждую запись.</p><h3>Трипвайр-алерты на 20 самых важных эндпоинтов</h3><p>Деградация производительности подкрадывается незаметно — инкрементальные фичи постепенно ломают индексы и кэши. Установите порог в несколько стандартных отклонений от нормы на ключевых эндпоинтах. Команда узнает о проблеме в течение часов после деплоя, а не через неделю жалоб.</p><h3>Громко падайте при ошибках</h3><p>Не глушите исключения except: pass. Паттерн от Джека: <i>крашиться локально, мягко отказывать в проде — но всегда отправлять ошибку в Sentry/логи</i>. Чем раньше заметили — тем меньше ущерб.</p><h2>Про работу в команде</h2><h3>Не обвиняйте одного человека за баг в проде</h3><p>Любой инцидент — это каскад минимум трёх провалов: автор не заметил, ревьюер пропустил, QA не покрыло, тесты не проверили, архитектура допустила класс ошибки. Обвинение одного человека мешает команде видеть системные причины.</p><h3>Все разработчики должны быть немного фулстек</h3><p>В MorphMarket убрали строгое деление на фронтенд/бэкенд/ops. Переобучение заняло год, но окупилось: фронтендер добавляет поле в БД сам, а не ждёт бэкендера — это сокращает время доставки фич.</p><h3>Пятницы технического долга</h3><p>Разумный компромисс между CEO, который хочет фичи, и инженерами, которым нужна чистота кода: с понедельника по четверг приоритеты задаёт продукт, а по пятницам — CTO. Бонус: инженеры отдыхают на глубоких задачах перед выходными.</p><h3>Запретите длинные PR</h3><p>В MorphMarket был период, когда PR висели по 3 месяца и накапливали тысячи изменений. Их невозможно ревьюить, они конфликтуют с master и создают огромный деплой-риск. Новое правило: мерж в master каждые 1–2 дня, максимум — раз в неделю. Побочный эффект: старшие разработчики дают фидбэк раньше, и драматические переписывания случаются реже.</p><h3>Поощряйте общение в удалённых командах</h3><p>Джек начал с ежедневных постов о прогрессе, потом стал публично задавать вопросы (даже на которые знал ответ), делиться изменениями в коде и просить мнения. Это постепенно снизило барьер и сделало общение нормой, а не исключением.</p><h2>Про производительность</h2><h3>N+1-запросы — враг номер один в ORM</h3><p>Проблема проявляется на крупных эндпоинтах, которые постепенно расширяют разные авторы: новые фильтры и параметры создают спорадические N+1, которые возникают только при определённом сочетании фильтров и объёме данных. Настройте APM (например, <a href="https://newrelic.com/">New Relic</a>) для автоматического обнаружения N+1 и обучите команду ловить их на ревью.</p><h3>Кэширование — последняя мера</h3><p>Код кэширования чудовищно сложен в поддержке. Сначала исправьте модель данных, допишите индексы, уберите N+1, оптимизируйте алгоритмы. И только после этого — кэш. Джек рассказывает о случаях, когда наивный кэш работал на малых объёмах, но убивал Redis (он однопоточный!) при росте данных на два порядка.</p><h3>Таймауты на всех уровнях — иммунная система сервиса</h3><p>Веб-запрос должен завершиться за 25 секунд — иначе убивается. Да, для длинных задач придётся использовать фоновые задачи, но без таймаутов одна тормозящая внешняя API может съесть все потоки сервера и уронить всё.</p><h3>Выгружайте работу в фоновые задачи</h3><p>Отправка email, push-уведомления, обработка вебхуков от платёжных систем — всё это должно уходить в очередь. Веб-процесс обязан оставаться быстрым.</p><h3>ORDER BY id вместо ORDER BY created</h3><p>Если поле created неизменяемо и совпадает по порядку с id — сортируйте по id. Индекс на id уже есть бесплатно в каждой таблице.</p><h3>Удаляйте ненужные данные</h3><p>В MorphMarket некоторые таблицы копили 10 лет данных — логи IP-адресов, записи push-уведомлений, сообщения девятилетней давности. Скрипт очистки делает эти таблицы компактнее и быстрее.</p><h2>Про безопасность и надёжность</h2><h3>Начните с модели угроз: что может получить атакующий?</h3><p>Для MorphMarket основные вектора: захват аккаунтов (фикс — обязательный 2FA), спам-фишинг (капча на регистрации + фильтры сообщений), DDoS (<a href="https://www.cloudflare.com/">Cloudflare</a> + rate limit на nginx + кэш ключевых эндпоинтов). Универсального подхода нет — каждый сервис должен понимать свою специфику.</p><h3>CHECK-ограничения в БД — лучше предотвратить, чем чинить</h3><p>Раньше в MorphMarket крон-задача раз в сутки проверяла целостность данных и слала алерты. Всё заменили на CHECK constraints на уровне БД — некоторые проверяют до 16 операций над разными колонками. Невалидные данные просто не могут быть записаны.</p><h3>Вебхуки — рассадник race conditions</h3><p>Действие в коде может вызвать вебхук, который прилетит быстрее, чем данные запишутся в БД. Получатель вебхука прочитает устаревшее состояние и может затереть свежие данные. Решение: откладывайте действия, вызывающие вебхуки, до on_commit в БД.</p><h3>Логируйте намерение перед побочным эффектом — особенно для платежей</h3><p>Перед списанием с карты или возвратом пишите в лог: <i>"bill user XYZ $100 for Y"</i>. Простой Write-Ahead Log спасал MorphMarket в нескольких инцидентах.</p><h2>Про деплой</h2><h3>Деплой должен быть быстрым — действительно быстрым</h3><p>MorphMarket сократил деплой с 28 минут до 2. Как: разделили фронтенд и бэкенд, распараллелили, перешли на Rust-based компиляцию JS, вынесли сборку на мощные машины (MacBook M-series вместо commodity Heroku), убрали из слага всё ненужное, почистили старые ассеты из S3.</p><h3>Smoke-тест после каждого деплоя в прод</h3><p>Автоматическая проверка ключевых страниц через <a href="https://playwright.dev/">Playwright</a> после каждого деплоя. Если последние 150 деплоев были скучными — соблазн перестать проверять вручную огромен. Автоматический smoke-тест не устаёт и не теряет бдительность.</p><h3>Не деплойте ничего рискованного в пятницу</h3><p>Баг может проявиться вечером или в субботу, когда никого нет. Мелкие хотфиксы и изменения в админке — можно. Всё остальное — до четверга.</p><h3>Плейбук для инцидентов при деплое</h3><p>Короткие инструкции: как убить залипшие соединения к БД, точечно почистить кэш (без flush, который разлогинит всех), форсировать обновление ассетов. Обученная команда сокращает даунтайм в разы.</p><h2>Про карьерный рост — от тимлида до CTO</h2><h3>Научитесь говорить о производительности сотрудников</h3><p>Джек признаётся, что панически боялся этих разговоров, потому что не любит конфликты. Но реальность проста: лучше открыто сказать человеку, где он не дотягивает, чем копить раздражение и уволить неожиданно. Большинство людей способны расти под менторингом — Джек это недооценивал.</p><h3>Документ ожиданий от сотрудника</h3><p>Если написать <i>"мы ценим, когда вы решаете проблемы в #bug-discussion до того, как они дойдут до CTO"</i>, люди действительно начнут так делать. Мотивированный сотрудник хочет ясности. Он не может прочитать ваши мысли.</p><h3>Одно крупное изменение за раз</h3><p>Внедряете E2E-тесты? Доведите до стабильности, прежде чем браться за линтеры. Если система сделана плохо — команда не будет ей доверять и не будет использовать. Плюс есть предел того, сколько изменений люди могут усвоить одновременно.</p><h3>CLI-инструменты + AI = максимальный рычаг</h3><p>AI отлично работает с текстом. CLI-инструменты тоже. Дайте AI доступ к продакшн-логам, отчётам об ошибках, read-only реплике БД, CI-результатам и REPL — и получите огромный множитель продуктивности. Джек называет это <i>"наблюдаемость через CLI — ваш главный усилитель для AI"</i>.</p><h2>FAQ</h2><h3>Почему монолит, а не микросервисы?</h3><p>Монолит на миллион строк с 20 разработчиками — рабочая модель, если соблюдать дисциплину. Микросервисы добавляют сетевую сложность, и для команды такого размера overhead координации перевешивает выгоды. Джек не утверждает, что микросервисы плохи — он утверждает, что переход оправдан только при конкретной боли, а не как дефолтный выбор.</p><h3>С чего начать, если в проекте уже бардак?</h3><p>С Sentry до inbox zero и таймаутов на веб-запросы. Первое даёт видимость реальных проблем, второе предотвращает каскадные отказы. Дальше — по статье: N+1, изоляция тестов, сервисные объекты.</p><h3>Насколько эти уроки применимы за пределами Django?</h3><p>Джек 17 лет работал с Rails, Laravel, Express и другими фреймворками — и подчёркивает, что большинство уроков переносимы. N+1, дефолтные сортировки, race conditions в вебхуках, инфраструктура как код — всё это не привязано к конкретному стеку.</p><h3>Правда ли, что E2E-тесты лучше юнит-тестов?</h3><p>Не <i>лучше</i>, а дают больше отдачи на первом этапе. Один E2E-тест на happy path покрывает весь стек. Юнит-тесты нужны для деталей и граничных случаев, но начинать покрытие лучше сверху вниз.</p><h2>Выводы</h2><p>113 уроков Джека Кинселлы — это не теория из книг, а выжимка из 7 лет ежедневной работы над монолитом, который обслуживает реальных пользователей. Главные принципы:</p><ul><li>Предотвращайте, а не чините — CHECK constraints, таймауты, линтеры</li><li>Делайте невидимое видимым — логи, мониторинг, Sentry до inbox zero</li><li>Инвестируйте в скорость обратной связи — быстрый деплой, быстрый CI, короткие PR</li><li>Доверяйте людям и системе, а не контролю — fullstack-навыки, плейбуки, документ ожиданий</li></ul><p>Оригинальная статья: <a href="https://www.semicolonandsons.com/articles/scaling-a-monolith-to-1m-loc-113-pragmatic-lessons-from-tech-lead-to-cto">Scaling a Monolith to 1M LOC: 113 Pragmatic Lessons from Tech Lead to CTO</a> (Semicolon &amp; Sons, Jack Kinsella).</p><p>Если знаете коллегу, который борется с растущим монолитом — отправьте ему эту статью. А в комментариях расскажите, какой урок оказался самым неожиданным для вас.</p>]]></content:encoded>
    </item>
    <item>
      <title>Переписали JSONata на Go с помощью ИИ за день — экономия $500K в год</title>
      <link>https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v</link>
      <comments>https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v</guid>
      <description><![CDATA[<p>Инженер из Reco переписал JSONata на Go за 7 часов с ИИ. Библиотека gnata дала ускорение до 1000x и сэкономила $500 000 в год на инфраструктуре Kubernetes.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/perepisali-jsonata-na-go-s-pomoshhyu-ai-za-den---ekonomiya--500k-v">Переписали JSONata на Go с помощью ИИ за день — экономия $500K в год</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Golang]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Один инженер. Семь часов работы. $400 на токены для ИИ. Результат — чистая Go-библиотека, которая заменила целый флот Node.js-подов в Kubernetes и сэкономила компании $500 000 в год на инфраструктуре.</p><p>Это не абстрактный кейс из маркетингового блога. Это реальная история от <a href="https://www.reco.ai/">Reco</a> — SaaS-платформы безопасности, которая обрабатывает миллиарды событий в день. Их инженер Нир Барак взял подход, описанный Cloudflare в статье о <a href="https://blog.cloudflare.com/how-we-rebuilt-nextjs-with-ai-in-one-week/">переписывании Next.js с помощью ИИ</a>, и применил его к собственной инфраструктуре. Получилась библиотека <a href="https://github.com/RecoLabs/gnata">gnata</a> — полная реализация JSONata 2.x на чистом Go с ускорением до 1000x на типовых выражениях.</p><p><a href="https://jsonata.org/">JSONata</a> — это язык запросов и трансформации JSON-данных. Его можно представить как jq, но с поддержкой лямбда-функций и более выразительным синтаксисом. JSONata позволяет писать сложные правила обработки данных без необходимости лезть в код основного приложения. Это делает его популярным в системах, где бизнес-аналитики или исследователи пишут правила детекции, а инженеры обеспечивают инфраструктуру.</p><blockquote>Главное из статьи:<br /><br />- JSONata-выражения через RPC обходились в $300 000/год на инфраструктуру<br />- ИИ-ассистированная перезапись на Go заняла 7 часов и стоила $400 в токенах<br />- Результат: 13 000 строк Go-кода, 1 778 пройденных тестов из официального набора<br />- Ускорение до 1000x на простых выражениях за счёт устранения RPC и JSON-парсинга<br />- Суммарная экономия после оптимизации пайплайна — $500 000/год</blockquote><h2>Проблема — JSONata как узкое место</h2><p>У Reco есть policy engine, который проверяет JSONata-выражения на каждом событии в потоке данных. Миллиарды событий, тысячи различных выражений. Исследователи безопасности пишут правила детекции на JSONata, а инженеры обеспечивают их выполнение в реальном времени.</p><p>Загвоздка: <b>референсная реализация JSONata написана на JavaScript</b>, а весь пайплайн Reco — на Go. Годами компания держала в Kubernetes отдельный флот Node.js-подов (jsonata-js), к которым Go-сервисы обращались через RPC.</p><p>Для каждого события и каждого выражения это означало:</p><ol><li>Сериализовать данные на стороне Go</li><li>Отправить по сети в Node.js-под</li><li>Выполнить JSONata-выражение</li><li>Сериализовать результат</li><li>Отправить обратно в Go-сервис</li></ol><h3>Во что это обходилось</h3><p>Прямые затраты на инфраструктуру jsonata-js составляли <b>около $300 000 в год</b>, и сумма росла с каждым новым клиентом и правилом детекции. На одном из крупных кластеров количество реплик jsonata-js перевалило за 200 — и команда столкнулась с лимитом IP-адресов в Kubernetes.</p><p>Но денежные затраты были даже не главной проблемой. RPC round-trip занимает ~150 микросекунд ещё до начала вычисления. Для простого выражения вроде user.email = "admin@co.com", которое должно выполняться за наносекунды, основная цена — пересечение языковой границы. На масштабе Reco эти микросекунды складываются в ощутимые задержки.</p><p>Команда пробовала разные подходы: оптимизировала выражения, кэшировала результаты, даже встраивала V8 прямо в Go-процесс. Всё это давало инкрементальные улучшения, но не решало корневую проблему — <b>данные всё равно пересекали языковую границу</b>.</p><h2>Решение — переписать на Go с помощью ИИ</h2><p>Толчком стала статья Cloudflare о том, как один инженер за неделю переписал API Next.js на Vite, потратив $1 100 на токены. Нир Барак прочитал её и понял: у Reco та же самая ситуация — есть спецификация, есть тестовый набор, нужна реализация на другом языке.</p><h3>Подход: спецификация + тесты = контракт</h3><p>Метод прямолинейный:</p><ol><li>Портировать официальный тестовый набор jsonata-js на Go</li><li>Направить ИИ на реализацию, пока все тесты не пройдут</li><li>Добавить оптимизации, специфичные для Go и задачи Reco</li></ol><p>В выходные Нир составил план, разбитый на «волны» (с помощью ИИ). На следующий день нажал «старт».</p><h3>Инструменты и затраты</h3><p>Конкретные модели и инструменты автор не раскрывает, но ключевые цифры говорят сами за себя:</p><ul><li><b>Время работы:</b> 7 часов (один инженер)</li><li><b>Стоимость токенов:</b> $400</li><li><b>Результат:</b> 13 000 строк Go-кода</li><li><b>Тесты:</b> 1 778 пройденных тест-кейсов из официального набора jsonata-js</li></ul><p>Библиотека получила название <a href="https://github.com/RecoLabs/gnata">gnata</a> — полная реализация спецификации JSONata 2.x на чистом Go.</p><h2>Технические результаты</h2><h3>Двухуровневая архитектура вычислений</h3><p>gnata использует двухуровневую архитектуру оценки выражений. На этапе компиляции каждое выражение анализируется и классифицируется.</p><p><b>Быстрый путь (fast path)</b> обрабатывает простые выражения: поиск по полям, сравнения и 21 встроенную функцию на чистых путях (например, $exists(a.b) или $lowercase(name)). Эти выражения вычисляются прямо по сырым JSON-байтам, без полного парсинга документа. Для выражения вроде account.status = "active" результат — <b>ноль аллокаций в куче</b>.</p><p><b>Полный путь (full path)</b> — полноценный парсер и вычислитель с полной семантикой JSONata 2.x. Он парсит JSON, но только нужные поддеревья, а не весь документ целиком.</p><h3>Потоковая обработка</h3><p>Поверх двухуровневой оценки работает потоковый слой (StreamEvaluator), спроектированный под конкретную задачу Reco: применить N скомпилированных выражений к каждому событию, где события структурно похожи.</p><ul><li>Все пути из всех выражений объединяются в один проход по данным — количество выражений не влияет на скорость чтения</li><li>После прогрева горячий путь работает без блокировок (lock-free)</li><li>Планы вычислений создаются один раз на схему событий и кэшируются неизменяемо — чтение через один атомарный load без синхронизации</li><li>Память ограничена: кэш с настраиваемой ёмкостью, вытесняющий старые записи</li></ul><h3>Бенчмарки</h3><p>Ускорение на простых выражениях — в основном за счёт полного устранения RPC: gnata вычисляет прямо по сырым байтам без парсинга JSON. На сложных выражениях, где нужен полный парсинг и AST-вычисление, разрыв сужается, но они всё равно <b>в 25–90 раз быстрее</b>, чем через RPC.</p><p>На простых lookups ускорение достигает <b>1000x</b>.</p><h3>Пример использования</h3><h2>Экономический эффект</h2><h3>Этап 1: устранение RPC-флота</h3><p>Первый и самый очевидный эффект — полное устранение jsonata-js подов в Kubernetes:</p><ul><li><b>До:</b> ~$25 000/мес на compute для jsonata-js (200+ реплик Node.js)</li><li><b>После:</b> $0 — gnata работает как библиотека внутри существующих Go-сервисов</li><li><b>Экономия:</b> ~$300 000/год</li></ul><h3>Этап 2: рефакторинг rule engine</h3><p>Но на этом история не закончилась. Старый JSONata через RPC мог обрабатывать только одно выражение за раз, и вся инфраструктура вокруг него была вынуждена подстраиваться. Rule engine запускал десятки тысяч горутин для максимизации параллелизма — со всеми вытекающими: избыточное потребление памяти и высокая конкуренция за CPU.</p><p>gnata не имеет таких ограничений. Команда заменила внутренности rule engine на более простую и эффективную реализацию с just-in-time батчингом (паттерн request coalescing), короткоживущими кэшами и групповыми запросами обогащения данных.</p><p>Результат: ещё <b>~$18 000/мес</b>, или <b>~$200 000/год</b> экономии.</p><h3>Итого</h3><p><b>$500 000/год</b> снято с пайплайна за две недели работы. Из них 7 часов — создание gnata, остальное — тестирование, code review и раскатка.</p><h2>Как внедряли — от shadow mode до продакшена</h2><p>Создать библиотеку — половина дела. Вторая половина — убедиться, что она выдаёт ровно те же результаты, что и оригинал, на миллиардах реальных событий.</p><p>У Reco уже была инфраструктура для shadow-тестирования: feature flags, параллельное вычисление, логирование расхождений. Подключить gnata к ней было несложно.</p><p>Хронология раскатки:</p><ul><li><b>День 1:</b> gnata готова, PR открыт</li><li><b>Дни 2–6:</b> code review, QA на реальных продакшен-выражениях, деплой в preprod в shadow mode. gnata вычисляет всё, но результаты jsonata-js остаются основными. Расхождения логируются и алертятся. Найденные edge-кейсы исправляются</li><li><b>День 7:</b> три дня подряд ноль расхождений. gnata переведена в primary</li></ul><p>К моменту перевода gnata уже обработала миллиарды событий и выдала идентичные результаты. Более того, в процессе обнаружились баги в самом jsonata-js — случаи, где референсная реализация не соответствует собственной спецификации.</p><p>Побочный эффект: gnata стала одним из первых крупных PR в Reco, где ИИ-агенты ревьюили ИИ-сгенерированный код. Агенты помечали всё подряд — и реальные проблемы с конкурентностью, и косметические замечания. Команде пришлось научить их отличать одно от другого, и этот опыт теперь используется шире.</p><h2>Уроки — когда ИИ-переписывание работает</h2><p>gnata — удачный кейс. Но не каждая задача подходит для ИИ-переписывания. Вот что сделало этот проект идеальным кандидатом:</p><ol><li><b>Чёткая спецификация.</b> JSONata имеет формальную спецификацию и обширный тестовый набор. ИИ не нужно придумывать поведение — нужно реализовать заданное</li><li><b>Детерминированная проверка.</b> Каждое выражение имеет однозначный правильный ответ. Можно автоматически проверить корректность — не нужен человек для оценки каждого результата</li><li><b>Глубокая экспертиза инженера.</b> Нир не просто «нажимал кнопку». Он спроектировал двухуровневую архитектуру, потоковую обработку и стратегию кэширования. ИИ генерировал код, но архитектурные решения принимал человек</li><li><b>Существующая инфраструктура тестирования.</b> Shadow mode, feature flags, сравнение результатов — всё это было готово до gnata. Без этого раскатить ИИ-сгенерированный код в продакшен было бы рискованно</li><li><b>Ограниченная область.</b> JSONata — это чётко ограниченная задача. Не «перепишите весь бэкенд», а «реализуйте этот конкретный язык на другом стеке»</li></ol><p>Как отметил Андрей Карпати: программирование становится неузнаваемым, и на верхних уровнях глубокая техническая экспертиза — «ещё больший мультипликатор, чем раньше, из-за увеличенного рычага». gnata — хорошая иллюстрация этой мысли.</p><h2>FAQ</h2><h3>Что такое JSONata и зачем он нужен?</h3><p>JSONata — это язык запросов и трансформации для JSON-данных. Он позволяет писать выражения для фильтрации, преобразования и агрегации JSON без написания кода на языке общего назначения. Используется в IoT-платформах, ETL-пайплайнах и системах обработки событий.</p><h3>Почему нельзя было просто оптимизировать существующий подход?</h3><p>Reco пробовали: оптимизация выражений, кэширование, встраивание V8 в Go. Всё это давало инкрементальные улучшения, но корневая проблема — пересечение языковой границы через RPC — оставалась. Единственное радикальное решение — нативная реализация на Go.</p><h3>Насколько gnata совместима с оригинальной JSONata?</h3><p>gnata проходит 1 778 тест-кейсов из официального набора jsonata-js и 2 107 интеграционных тестов в продакшен-обёртке Reco. В процессе тестирования были обнаружены баги в самом jsonata-js, где референсная реализация не соответствует собственной спецификации.</p><h3>Можно ли повторить этот подход в другом проекте?</h3><p>Да, если есть формальная спецификация и тестовый набор. Подход Cloudflare (vinext) и Reco (gnata) одинаков: берём спеку + тесты, направляем ИИ на реализацию до прохождения всех тестов. Ключевое требование — детерминированная проверка результата.</p><h2>Выводы</h2><p>История gnata — это не про то, что ИИ «волшебным образом» пишет продакшен-код. Это про то, как опытный инженер с глубоким пониманием задачи использует ИИ как мультипликатор своей экспертизы.</p><p>Ключевые выводы:</p><ul><li><b>ИИ-переписывание работает</b>, когда есть чёткая спецификация и автоматизированная проверка</li><li><b>Экономический эффект может быть огромным:</b> $400 на токены превратились в $500 000/год экономии</li><li><b>Архитектура важнее генерации кода:</b> двухуровневая оценка, потоковая обработка и батчинг — решения инженера, не ИИ</li><li><b>Shadow mode обязателен:</b> ИИ-сгенерированный код нельзя просто «выкатить» — нужна параллельная проверка на реальных данных</li><li><b>ROI считается просто:</b> если задача имеет спеку и тесты, стоимость ИИ-переписывания предсказуема</li></ul><p>gnata доступна как open-source библиотека: <a href="https://github.com/RecoLabs/gnata">github.com/RecoLabs/gnata</a>.</p><p><i>Источник: <a href="https://reco.ai/blog/we-rewrote-jsonata-with-ai">We Rewrote JSONata with AI</a> — блог Reco.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Как агенты Stripe отправляют 1300 PR в неделю без единой строчки человеческого кода</title>
      <link>https://tproger.ru/translations/kak-agenty-stripe-otpravlyayut-1-300-pr-v-nedelyu-bez-edinoj-strochk</link>
      <comments>https://tproger.ru/translations/kak-agenty-stripe-otpravlyayut-1-300-pr-v-nedelyu-bez-edinoj-strochk?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/kak-agenty-stripe-otpravlyayut-1-300-pr-v-nedelyu-bez-edinoj-strochk</guid>
      <description><![CDATA[<p>Как устроена система ИИ-агентов Minions в Stripe: архитектура блюпринтов, изолированные devbox-ы и MCP-инструменты, которые позволяют мержить 1300 PR в неделю.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/kak-agenty-stripe-otpravlyayut-1-300-pr-v-nedelyu-bez-edinoj-strochk">Как агенты Stripe отправляют 1300 PR в неделю без единой строчки человеческого кода</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[Автоматизирование]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>Каждую неделю <a href="https://stripe.com">Stripe</a> мержит более 1300 пулл-реквестов, в которых нет ни одной строчки, написанной человеком. Эти PR создают внутренние ИИ-агенты под названием Minions — они работают полностью автономно, без присмотра инженера.</p><p>Инженер отправляет сообщение в Slack, уходит за кофе — и возвращается к готовому пулл-реквесту, который уже прошёл автотесты и ждёт ревью. Пять задач за то время, которое обычно уходит на две. Звучит как фантастика, но за этой продуктивностью стоит не столько модель, сколько инфраструктура, которую Stripe строил годами — задолго до эры LLM.</p><p>Разбираемся, как устроена система Minions, какие задачи она решает и чему из опыта Stripe могут научиться другие компании. Статья основана на публичных материалах <a href="https://blog.bytebytego.com/p/how-stripes-minions-ship-1300-prs">ByteByteGo</a> и <a href="https://stripe.com/blog/engineering">Stripe Engineering Blog</a>.</p><p><b>Ключевые выводы:</b><br />— Stripe мержит более 1300 PR в неделю, созданных полностью автономными ИИ-агентами<br />— Minions — это unattended-агенты: они не требуют наблюдения или пошагового одобрения<br />— Архитектура построена на «блюпринтах» — гибридах жёсткого пайплайна и агентных циклов<br />— Главный секрет успеха — не модель, а developer-инфраструктура: изолированные окружения, быстрые тесты и чёткие ограничения<br />— Если агент не справился за два цикла CI — задача возвращается человеку</p><h2>Масштаб — цифры и факты</h2><p>Stripe обрабатывает более триллиона долларов платежей в год. Кодовая база компании — сотни миллионов строк кода, преимущественно на Ruby с типизацией <a href="https://sorbet.org">Sorbet</a>. Это относительно редкий стек, с которым LLM практически не сталкивались при обучении.</p><p>На этом фоне цифры Minions выглядят особенно впечатляюще:</p><ul><li><b>1300+ PR в неделю</b> — полностью автоматические, без единой строки человеческого кода</li><li><b>Типы задач</b> — миграции кода, обновление зависимостей, исправления безопасности, рефакторинг</li><li><b>Время запуска агента</b> — менее 10 секунд от запроса до начала работы</li><li><b>Параллельность</b> — один инженер может запустить 5-6 агентов одновременно</li></ul><p>Важно понимать: Stripe не считает каждый PR идеальным. Частично корректный пулл-реквест, который инженер доработает за 20 минут, — это уже серьёзный выигрыш. Система спроектирована с учётом этой реальности.</p><h2>Архитектура агентов</h2><h3>Изолированные окружения — devbox-ы</h3><p>Автономному агенту нужны три свойства от рабочей среды: <b>изоляция</b> (ошибки не затрагивают прод), <b>параллельность</b> (несколько агентов работают одновременно) и <b>предсказуемость</b> (каждый агент стартует с чистого состояния).</p><p>Stripe уже имел всё это. Их «devbox-ы» — облачные машины, предзагруженные с полной кодовой базой, инструментами и сервисами. Они поднимаются за 10 секунд, потому что Stripe заранее провизионирует и прогревает пул машин — клонирует репозитории, прогревает кеши и запускает фоновые сервисы. Инженеры и раньше использовали по одному devbox-у на задачу, а один инженер мог держать полдюжины одновременно. Агенты просто встроились в этот паттерн.</p><p>Поскольку devbox-ы работают в QA-окружении, они уже изолированы от продакшен-данных и реальной пользовательской информации. Агенты получают полные права без подтверждений — радиус поражения любой ошибки ограничен одной одноразовой машиной.</p><h3>Блюпринты — гибрид workflow и агента</h3><p>Есть два классических подхода к оркестрации LLM-систем:</p><ul><li><b>Workflow</b> — фиксированный граф шагов, где каждый шаг делает одну конкретную вещь, а последовательность предопределена</li><li><b>Agent</b> — цикл, в котором LLM сама решает, что делать дальше, на основе результатов предыдущих действий</li></ul><p>Workflow предсказуемы, но негибки. Агенты гибки, но ненадёжны. Stripe построил нечто среднее — «блюпринты» (blueprints).</p><p>Блюпринт — это последовательность узлов, в которой одни узлы выполняют детерминированный код, а другие запускают агентный цикл. Представьте структуру, которая чередует жёсткие и креативные шаги:</p><ul><li>Шаг «реализуй фичу» или «исправь ошибки CI» — полный агентный цикл с инструментами и свободой действий</li><li>Шаг «запусти линтеры» — захардкожен, всегда выполняется одинаково</li><li>Шаг «запуши ветку» — захардкожен, строго по шаблону PR компании</li></ul><p>Такое разделение экономит токены, снижает количество ошибок и гарантирует, что критические шаги выполняются каждый раз. При сотнях запусков в день каждый детерминированный узел — это одна проблема меньше, и этот эффект накапливается.</p><h3>Контекст — как не утонуть в сотнях миллионов строк</h3><p>У LLM ограниченное контекстное окно. Если загрузить все правила и конвенции глобально, контекст заполнится до того, как агент начнёт работать. Stripe использует глобальные правила «очень осторожно» — вместо этого они привязывают правила к конкретным директориям и паттернам файлов. Когда агент перемещается по файловой системе, он автоматически подхватывает только релевантные правила.</p><p>Для информации, которая не живёт в файловой системе, Stripe построил централизованный сервер <b>Toolshed</b>. Он хостит почти <b>500 инструментов</b> через <a href="https://modelcontextprotocol.io">MCP</a> (Model Context Protocol) — открытый стандарт, который даёт агентам единый способ вызывать внешние сервисы. Через MCP агенты получают доступ к внутренней документации, деталям тикетов, статусам билдов, результатам поиска по коду и многому другому.</p><p>Но больше инструментов — не значит лучше. Агенты работают лучше с тщательно подобранным подмножеством, релевантным их задаче. Stripe даёт Minions небольшой набор по умолчанию и позволяет инженерам добавлять дополнительные при необходимости.</p><h3>Модели и обратная связь</h3><p>Stripe не раскрывает конкретные модели, которые используют Minions, но архитектура сознательно абстрагирована от конкретной LLM. Ключевое значение имеет не модель, а инфраструктура обратной связи:</p><ol><li><b>Локальный линтинг</b> — запускается при каждом push за менее чем 5 секунд. Фоновый демон заранее вычисляет, какие правила линтинга применимы, и кеширует результаты</li><li><b>CI</b> — выборочно запускает тесты из батареи в 3+ миллиона тестов. Автофиксы применяются автоматически для известных паттернов ошибок</li><li><b>Повторная попытка</b> — если после автофикса ошибки остались, агент получает ещё один шанс исправить и запушить</li></ol><p>Затем — стоп. <b>Максимум два цикла CI.</b> Если код не проходит после второго push-а, ветка возвращается инженеру. Это ограничение осознанное: LLM показывают убывающую отдачу при повторных попытках решить ту же проблему. Больше раундов — больше токенов и вычислений без пропорционального улучшения.</p><h2>Какие задачи решают агенты</h2><p>Minions лучше всего справляются с задачами, которые хорошо определены, повторяемы и поддаются автоматической проверке:</p><ul><li><b>Миграции кода</b> — перевод со старых API на новые, обновление паттернов использования библиотек</li><li><b>Обновление зависимостей</b> — bump версий, адаптация кода к breaking changes</li><li><b>Исправления безопасности</b> — применение патчей по известным уязвимостям</li><li><b>Рефакторинг</b> — переименование, перемещение модулей, приведение к единому стилю</li><li><b>Мелкие баг-фиксы</b> — исправления, которые накапливаются в on-call очереди за ночь</li></ul><p>Чего Minions <b>не делают</b>:</p><ul><li>Не проектируют новую архитектуру</li><li>Не принимают продуктовых решений</li><li>Не пишут код, требующий глубокого понимания бизнес-логики</li><li>Не работают с задачами, которые нельзя проверить автоматическими тестами</li></ul><p>Это ключевое отличие от «attended» инструментов вроде <a href="https://cursor.com">Cursor</a> или <a href="https://claude.ai/code">Claude Code</a>, которые работают вместе с разработчиком. Инженеры Stripe используют и те, и другие — для разных типов задач.</p><h2>Результаты и метрики</h2><p>Stripe не публикует детальные внутренние метрики, но из публичных материалов и выступлений инженеров можно выделить ключевые результаты:</p><ul><li><b>1300+ PR в неделю</b> — объём, эквивалентный работе десятков инженеров</li><li><b>Сдвиг от написания к ревью</b> — инженеры не исчезли, но их роль изменилась: вместо написания кода они проверяют код, созданный агентами</li><li><b>Частично корректные PR</b> — даже неидеальные результаты экономят время, потому что доработка занимает 20 минут вместо нескольких часов с нуля</li><li><b>Масштабирование без найма</b> — рутинные задачи снимаются с инженеров, позволяя команде фокусироваться на сложной архитектурной работе</li></ul><p>Четыре уровня системы, которые обеспечивают эти результаты:</p><ol><li>Изолированные окружения — безопасные параллельные рабочие пространства</li><li>Гибридная оркестрация — детерминированные гарантии плюс агентная гибкость</li><li>Курированный контекст — правильная информация без перегрузки</li><li>Быстрая обратная связь с жёсткими лимитами на итерации</li></ol><h2>Чему учит опыт Stripe</h2><p>Главный инсайт Stripe: инвестиции в продуктивность разработчиков, сделанные за годы до появления LLM, дали неожиданные дивиденды, когда агенты появились в рабочем процессе.</p><p>Уроки, которые можно вынести:</p><ol><li><b>Начинайте не с выбора модели, а с инфраструктуры.</b> Окружения разработчика, тестовая инфраструктура, пайплайны обратной связи — если они хороши, агенты получат от них выгоду. Если нет — никакая модель не спасёт</li><li><b>Разделяйте детерминированное и агентное.</b> Линтинг, push, создание PR — это не задачи для LLM. Захардкодьте то, что можно захардкодить</li><li><b>Ставьте жёсткие ограничения.</b> Максимум два цикла CI — один из самых важных дизайн-решений Stripe. Знать, когда остановиться, так же важно, как знать, с чего начать</li><li><b>Начинайте с рутины.</b> Миграции, обновления зависимостей, патчи безопасности — это идеальные первые кандидаты для автономных агентов</li><li><b>Человеческое ревью никуда не уходит.</b> Роль инженера смещается от написания кода к проверке кода. Это не замена людей, а усиление команды</li></ol><p>Когда стоит задуматься о внедрении подобного подхода? Если у вас уже есть: надёжная CI/CD-инфраструктура, изолированные окружения для разработки и достаточно рутинных задач, которые поддаются автоматизации.</p><h2>Часто задаваемые вопросы</h2><h3>Какую LLM используют Minions в Stripe?</h3><p>Stripe не раскрывает конкретную модель. Архитектура Minions сознательно абстрагирована от конкретной LLM — ключевую роль играет инфраструктура (devbox-ы, блюпринты, MCP-инструменты), а не сама модель. Это позволяет менять или обновлять модель без переписывания всей системы.</p><h3>Могут ли Minions заменить разработчиков?</h3><p>Нет. Minions решают рутинные, хорошо определённые задачи — миграции, обновления зависимостей, патчи. Архитектурные решения, продуктовое проектирование и задачи с нетривиальной бизнес-логикой по-прежнему требуют человека. Роль инженера смещается от написания к ревью кода.</p><h3>Можно ли повторить подход Stripe в небольшой компании?</h3><p>Принципы масштабируемы: изолированные окружения, гибрид детерминированных и агентных шагов, курированный контекст, жёсткие лимиты на итерации. Но для полноценной реализации нужна зрелая CI/CD-инфраструктура и достаточный объём повторяемых задач. Начните с малого — автоматизируйте один тип рутинной задачи и расширяйте по мере роста доверия к системе.</p><h3>Что такое MCP и зачем он нужен агентам?</h3><p><a href="https://modelcontextprotocol.io">Model Context Protocol</a> (MCP) — это открытый стандарт, который даёт ИИ-агентам единообразный способ вызывать внешние сервисы. Stripe использует MCP для подключения почти 500 внутренних инструментов — от поиска по коду до статусов билдов. Благодаря MCP агенты получают доступ к нужной информации через стандартизированный интерфейс.</p><h2>Выводы</h2><p>Опыт Stripe показывает: будущее разработки — это не замена инженеров агентами, а построение инфраструктуры, в которой агенты становятся ещё одним типом участников инженерного процесса. 1300 PR в неделю без единой строчки человеческого кода — это результат не прорыва в ИИ, а многолетних инвестиций в developer experience.</p><p>Ключевой вопрос не «какую модель выбрать», а «готова ли наша инфраструктура к агентам». Изолированные окружения, быстрые тесты, чёткие ограничения, курированный контекст — всё это можно и нужно строить уже сейчас, вне зависимости от того, когда именно вы запустите первого агента.</p><p>Если тема ИИ-агентов в разработке вам интересна, рекомендуем изучить оригинальные статьи в <a href="https://stripe.com/blog/engineering">Stripe Engineering Blog</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>ИИ-инструменты не ускорили разработку — потому что код никогда не был узким местом</title>
      <link>https://tproger.ru/translations/ai-instrumenty-ne-uskorili-razrabotku---potomu-chto-kod-nikogda-n</link>
      <comments>https://tproger.ru/translations/ai-instrumenty-ne-uskorili-razrabotku---potomu-chto-kod-nikogda-n?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Алексей Михайлишин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/ai-instrumenty-ne-uskorili-razrabotku---potomu-chto-kod-nikogda-n</guid>
      <description><![CDATA[<p>Николь Форсгрен объясняет парадокс ИИ-продуктивности: код генерируется быстрее, но доставка ПО не ускоряется. Разбираем фреймворк DevEx и метрики DORA.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/ai-instrumenty-ne-uskorili-razrabotku---potomu-chto-kod-nikogda-n">ИИ-инструменты не ускорили разработку — потому что код никогда не был узким местом</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Организация разработки]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 28 Mar 2026 07:52:11 GMT</pubDate>
      <content:encoded><![CDATA[<p>Индустрия потратила миллиарды на ИИ-инструменты для разработчиков. <a href="https://github.com/features/copilot">GitHub Copilot</a>, <a href="https://cursor.com">Cursor</a>, <a href="https://codeium.com">Codeium</a>, десятки других ассистентов пишут код за секунды. Но поставки программного обеспечения быстрее не стали. Время от коммита до продакшена в крупных компаниях по-прежнему измеряется неделями и месяцами. Николь Форсгрен, автор книги <a href="https://itrevolution.com/product/accelerate/">Accelerate</a> и создатель метрик DORA, объясняет почему: код никогда и не был узким местом.</p><p>В своём докладе на <a href="https://qconsf.com/">QCon San Francisco</a> в марте 2026 года Форсгрен представила концепцию "AI Productivity Paradox" — парадокса ИИ-продуктивности. Суть проста: если узкое место было не в написании кода, то ускорение написания кода ничего не даст. А если и даст — то лишь создаст ещё большее давление на те части процесса, которые и без того задыхались.</p><p>ИИ-ассистенты для программирования — это инструменты, которые используют большие языковые модели для автодополнения кода, генерации функций по описанию на естественном языке, рефакторинга и объяснения кода. Самые известные — <a href="https://github.com/features/copilot">GitHub Copilot</a>, <a href="https://cursor.com">Cursor</a>, <a href="https://codeium.com">Codeium</a> и <a href="https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview">Claude Code</a>. Их общее обещание — кратный рост продуктивности разработчиков. По данным маркетинговых материалов, разработчики пишут код "на 55% быстрее" или "в 2 раза продуктивнее". Но что именно считается продуктивностью?</p><blockquote><b>Ключевые выводы</b><br /><br />— ИИ-ассистенты ускоряют написание кода, но не ускоряют доставку программного обеспечения — потому что написание кода занимает лишь малую часть от времени полного цикла разработки<br />— Настоящие узкие места — CI/CD-пайплайны, код-ревью, ручные согласования, тестирование и деплой — не затрагиваются ИИ-ассистентами<br />— По метрикам DORA, высокопроизводительные команды деплоят несколько раз в день с lead time менее суток — и это результат системной работы с процессами, а не с инструментами генерации кода<br />— Фреймворк DevEx (feedback loops, flow state, cognitive load) помогает найти настоящие точки трения<br />— Вместо покупки нового ИИ-инструмента стоит провести аудит процесса доставки: где теряется время, почему, и как это устранить</blockquote><h2>Парадокс ИИ-продуктивности</h2><p>"Мы можем генерировать код за минуты, за секунды, — говорит Форсгрен. — Люди из любых подразделений могут "вайб-кодить" приложение и запушить его. Но деплой по-прежнему занимает дни. Я написала тут "дни" — это я была оптимисткой. Обычно это месяцы".</p><p>Парадокс в том, что ИИ не просто не решает проблему доставки — он делает её более заметной и болезненной. Когда код генерируется быстрее, очереди на код-ревью растут. CI-пайплайны захлёбываются. Тестовые наборы не справляются с нагрузкой. Форсгрен формулирует это так: "ИИ усиливает те же проблемы, которые существовали и раньше. Только теперь — быстрее".</p><p>Она приводит два показательных примера. В 2012 году в Knight Capital инженер выполнил рутинный деплой. Скрипт развёртывания реактивировал старый feature flag. Деплой был ручным. Автоматических тестов не было. За 45 минут компания потеряла 460 миллионов долларов. А в начале 2026 года разработчик по имени Джейсон использовал ИИ-ассистент <a href="https://replit.com/">Replit</a> для работы с базой данных. Он явно указал: "никаких изменений в live-данных". Установил code freeze. Результат? "Новые инструменты — те же старые проблемы. Только теперь быстрее".</p><h2>Почему код — не узкое место</h2><p>Форсгрен разделяет процесс разработки на "внутренний цикл" (inner loop) и "внешний цикл" (outer loop). Внутренний цикл — это то, что происходит на машине разработчика: написание кода, локальная отладка, запуск тестов. Внешний цикл — всё остальное: CI/CD-пайплайны, код-ревью, тестирование, согласования, деплой.</p><p>ИИ-инструменты ускоряют внутренний цикл. Но внешний цикл — где на самом деле теряется время — они не затрагивают.</p><p>"Написание кода больше не является узким местом, — подчёркивает Форсгрен. — Мы можем генерировать сколько угодно кода. Но это делает остальные точки трения ещё дороже и заставляет их торчать ещё сильнее. Справляются ли наши тестовые наборы с такой нагрузкой? Справляются ли наши CI-пайплайны? Могу ли я ревьюить достаточно пулл-реквестов для всего того шлака, который валится в систему? Не всегда".</p><p>Вот из чего складывается "невидимое" время доставки, которое ИИ не трогает:</p><ul><li><b>Код-ревью</b> — пулл-реквесты висят днями, потому что назначены не тому человеку, застряли в очереди или ревьюер в отпуске</li><li><b>CI/CD-пайплайны</b> — сборка падает, flaky-тесты, зависимости между сервисами</li><li><b>Ручные согласования</b> — деплой требует координации нескольких команд, инструментов, групповых решений о рисках</li><li><b>Тестирование</b> — автоматических тестов недостаточно, а ручное тестирование не масштабируется</li><li><b>Онбординг</b> — новый сотрудник на третьей неделе всё ещё ждёт доступ к базе данных</li><li><b>"Не моя зона ответственности"</b> — задачи буксуют на стыках между командами, процессами и системами</li></ul><h2>Что говорят данные</h2><p><a href="https://dora.dev/">DORA</a> (DevOps Research and Assessment) — это фреймворк из четырёх метрик, который Форсгрен разработала для измерения эффективности доставки ПО. Две метрики отвечают за скорость, две — за стабильность:</p><ul><li><b>Deployment frequency</b> — как часто команда деплоит в продакшен</li><li><b>Lead time for changes</b> — сколько времени проходит от коммита до запуска в продакшене</li><li><b>Change failure rate</b> — какой процент изменений требует отката или вмешательства</li><li><b>Mean time to restore (MTTR)</b> — как быстро команда восстанавливает сервис после сбоя</li></ul><p>У высокопроизводительных команд показатели выглядят так: деплой несколько раз в день (или по потребности бизнеса, а не из-за технических ограничений), lead time менее суток, change failure rate около 5%, восстановление после сбоя — менее часа.</p><p>"Эти команды не просто везучие, — говорит Форсгрен. — Они системно устранили трение". И она отмечает важный нюанс: это работает в компаниях любого размера и любой отрасли. Крупные компании говорят: "Это только для стартапов — у них нет нашего регулирования". Стартапы говорят: "Это только для крупных — у них ресурсы". Данные показывают, что и те, и другие неправы.</p><p>При этом ИИ-инструменты усиливают давление на внешний цикл. Форсгрен отмечает, что многие команды удваивают инвестиции во внутренний цикл (ещё больше ИИ-инструментов, ещё быстрее генерация), игнорируя тот факт, что внешний цикл "по-прежнему так же медленный, а может, и ещё медленнее".</p><p>Отдельно Форсгрен обращает внимание на метрику "строки кода" (lines of code): "Это всегда была бесполезная метрика. ИИ наконец-то сделал это очевидным для всех, потому что теперь кода генерируется слишком много. Он чрезмерно многословный, полон избыточных комментариев. Иногда лучшее, что можно сделать — это удалить код".</p><h2>Где реально теряется время</h2><p>Форсгрен приводит конкретные цифры стоимости трения. По данным McKinsey, 40% бюджетов на разработку уходят на устранимый "rework" — переделки, которых можно было избежать. Другое исследование показало, что разработчики ощущают свою продуктивность на уровне 68,5% — а недостающие 31,5% обходятся мировой экономике в 300 миллиардов долларов потерянного ВВП. Ещё одна оценка: 1,52 триллиона долларов теряется ежегодно из-за технического долга.</p><p>Форсгрен предлагает простую "расчётку на салфетке": 20 разработчиков, каждый теряет по 30 минут в день на одну точку трения. Это 10 часов в день, 2600 часов в год. При стоимости 100 долларов в час — 260 000 долларов в год. "Мы уже тратим это время, — говорит она. — Просто тратим его на борьбу с трением, а не на его устранение".</p><p>Типичные точки потери времени, которые ИИ-инструменты не затрагивают:</p><ol><li><b>Медленная сборка</b> — двухчасовой билд означает двухчасовое ожидание обратной связи. Разработчик переключает контекст, начинает параллельную задачу, теряет фокус</li><li><b>Процесс код-ревью</b> — отсутствие чётких владельцев, ручное назначение, неравномерная нагрузка между ревьюерами</li><li><b>Ручные деплои</b> — координация между командами, согласование рисков, ручные чек-листы</li><li><b>Flaky-тесты</b> — нестабильные тесты подрывают доверие к CI-пайплайну и замедляют обратную связь</li><li><b>Процессные задержки</b> — согласование с юристами, безопасниками, product-менеджерами, которое "всегда занимает столько времени"</li></ol><h2>Фреймворк DevEx — как найти настоящие проблемы</h2><p>Вместо абстрактной "продуктивности" Форсгрен предлагает работать с Developer Experience (DevEx) — опытом разработчика. Фреймворк DevEx состоит из трёх измерений:</p><ul><li><b>Feedback loops</b> (петли обратной связи) — сколько времени проходит от действия до результата? Сколько ждать, пока сборка завершится? Пока ревьюер ответит? Пока CI/CD-пайплайн отработает?</li><li><b>Flow state</b> (состояние потока) — может ли разработчик сосредоточиться на сложных задачах? Или его постоянно отвлекают переключения контекста, совещания, ожидание ответов?</li><li><b>Cognitive load</b> (когнитивная нагрузка) — сколько ментальной ёмкости уходит на "обслуживание" процесса? Исследования Глории Марк показывают, что максимум глубокой работы в день — около 4 часов. На что разработчик тратит эти 4 часа?</li></ul><p>Эти три измерения взаимосвязаны. Быстрая обратная связь сохраняет flow state и снижает когнитивную нагрузку. Низкая когнитивная нагрузка позволяет сосредоточиться и принимать решения быстрее. Защищённый flow state ускоряет обучение и накапливает результаты со временем.</p><p>ИИ меняет динамику flow state (состояния потока), отмечает Форсгрен: "Раньше мы могли заблокировать часы для глубокой работы над кодом. Теперь мы не просто пишем код — мы промптим, тут же получаем обратную связь, принимаем код, ревьюим, переписываем. Это как Stack Overflow на стероидах".</p><h3>Как начать: 7 шагов</h3><p>Форсгрен и Аби Нода (CEO компании DX) выделили семь шагов, которые проходят сотни компаний при улучшении DevEx:</p><ol><li><b>Поговорите с людьми</b> — прежде чем собирать метрики, просто спросите разработчиков: "Что вас бесит каждый день?"</li><li><b>Начните с малого</b> — выберите одну видимую, достижимую проблему, которую можно решить за недели, а не за кварталы</li><li><b>Соберите данные</b> — системная телеметрия, опросы, метрики результатов</li><li><b>Приоритизируйте через RICE</b> — Reach (охват) x Impact (влияние) x Confidence (уверенность) / Effort (усилия)</li><li><b>Продайте стратегию</b> — переведите технические проблемы на язык долларов и бизнес-результатов</li><li><b>Внедряйте изменения</b> — от локальных экспериментов в одной команде до масштабирования на всю организацию</li><li><b>Оцените и покажите ценность</b> — документируйте результаты и делитесь ими</li></ol><p>Форсгрен подчёркивает: начинать можно с любого шага в зависимости от зрелости организации. Но на любом этапе стоит "просто поговорить с несколькими людьми" — это самый быстрый и дешёвый способ получить достоверные данные.</p><h3>RICE: как приоритизировать улучшения</h3><p>Форсгрен рекомендует фреймворк RICE для выбора, какие проблемы решать первыми:</p><ul><li><b>Reach</b> — сколько людей затронет улучшение?</li><li><b>Impact</b> — насколько сильно оно улучшит их работу?</li><li><b>Confidence</b> — насколько мы уверены, что это выполнимо?</li><li><b>Effort</b> — сколько усилий потребуется? (чем меньше, тем лучше)</li></ul><p>Пример из доклада: три кандидата на улучшение — автоматизация flaky-тестов (высокий охват, высокое влияние, но огромные усилия), оптимизация процесса код-ревью (высокий охват, высокое влияние, низкие усилия) и мониторинговые дашборды (средний охват, средние усилия). Выбор очевиден: начать с код-ревью — "широкое влияние, высокая уверенность, быстрые результаты, не слишком много усилий".</p><h2>FAQ</h2><h3>ИИ-ассистенты для кода совсем бесполезны?</h3><p>Нет. ИИ-ассистенты действительно ускоряют написание кода, автодополнение, генерацию шаблонов и объяснение чужого кода. Проблема не в самих инструментах, а в завышенных ожиданиях: ускорение одного этапа (написание кода) не ускоряет весь процесс (доставку). По аналогии из теории ограничений: оптимизация не-узкого-места не улучшает пропускную способность системы.</p><h3>Какие метрики использовать вместо "строк кода"?</h3><p>Форсгрен рекомендует фреймворк <a href="https://queue.acm.org/detail.cfm?id=3595878">SPACE</a>: Satisfaction (удовлетворённость инструментами и процессами), Performance (качество результатов: pass rate тестов, частота сбоев), Activity (количественные метрики: PR, деплои — но не строки кода), Communication and Collaboration (взаимодействие: ревью, API-вызовы, совещания), Efficiency and Flow (время от действия до результата). Дополнительно — четыре метрики DORA для end-to-end доставки.</p><h3>Как убедить руководство инвестировать в DevEx, а не в новые ИИ-инструменты?</h3><p>Форсгрен приводит три стратегии. Первая — видимость и подотчётность: пример Дэйва Андерсона из Amazon, который создал ежемесячный отчёт для топ-менеджмента с рейтингом команд по ошибкам. "Через неделю директора бежали к нему в офис, чтобы убрать свою команду из списка". Вторая — простые данные с чёткими действиями: пример LinkedIn, где "Developer Insights Hub" не просто показывал метрики, а позволял детализацию до конкретной причины проблемы. Третья — перевод трения в доллары: пример Block, где посчитали стоимость каждой точки трения и сэкономили миллионы за 12 месяцев.</p><h3>Что делать прямо сейчас — одно конкретное действие?</h3><p>Для рядового разработчика — записывать в течение недели все моменты, когда теряется 30+ минут на "ерунду, которой не должно быть". Для тимлида — провести 30-минутную ретроспективу: "Что на этой неделе замедлило нас, но не было собственно работой?". Для руководителя — поговорить минимум с тремя людьми и посчитать стоимость трения "на салфетке".</p><h2>Выводы</h2><p>Главный тезис Форсгрен провокативен, но подкреплён данными: ИИ-инструменты для написания кода не ускорили доставку программного обеспечения, потому что написание кода никогда не было настоящим узким местом. Настоящие узкие места — это процессы, согласования, пайплайны, код-ревью и инфраструктура деплоя.</p><p>Организации, которые выигрывают в эпоху ИИ, не просто выдали всем Copilot или Cursor. Они системно устраняют трение, чтобы разработчики могли доставить код до клиента, провести эксперимент, получить обратную связь. "Все крупные компании, с которыми я работаю, — говорит Форсгрен, — сейчас активно ищут способы убрать это трение".</p><p>Вместо того чтобы покупать очередной ИИ-инструмент, стоит задать себе три вопроса: Что на самом деле замедляет доставку? Где разработчики теряют время на борьбу с процессом, а не на решение задач? И какое самое маленькое изменение может убрать самую большую точку трения?</p><p><i>Источник: <a href="https://www.infoq.com/news/2026/03/agoda-ai-code-bottleneck/">From Friction to Flow: How Great DevEx Makes Everything Awesome</a> — доклад Николь Форсгрен на QCon San Francisco, март 2026.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Из медтеха в Python-разработчики: как менторство помогло найти работу в IT</title>
      <link>https://tproger.ru/articles/iz-medteha-v-python-razrabotchiki--kak-mentorstvo-pomoglo-najti-rabotu-v-it</link>
      <comments>https://tproger.ru/articles/iz-medteha-v-python-razrabotchiki--kak-mentorstvo-pomoglo-najti-rabotu-v-it?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Даровская Маша]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/iz-medteha-v-python-razrabotchiki--kak-mentorstvo-pomoglo-najti-rabotu-v-it</guid>
      <description><![CDATA[<p>История перехода из медтеха в Python-разработку: как менторство помогло преодолеть сотни отказов и найти первую работу в IT. Советы по резюме, собеседованиям и выбору оффера от опытного наставника.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/iz-medteha-v-python-razrabotchiki--kak-mentorstvo-pomoglo-najti-rabotu-v-it">Из медтеха в Python-разработчики: как менторство помогло найти работу в IT</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Языки программирования]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Собеседование]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[1C]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 15 Sep 2025 12:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Женя Лопухов четыре года работал в нейрохирургическом стартапе и выгорел. Постоянное напряжение, высокая цена ошибки и бесконечный стресс вынудили его искать новую сферу, где можно было бы работать системно и спокойно. Так он оказался в мире Python-разработки.</p><p>Но переход в IT редко бывает лёгким. Первые месяцы поиска работы обернулись десятками отказов: более 110 отправленных резюме, более 500 откликов на HeadHunter, 200 на Хабр и около 50 – на вакансии в телеграм. И только одно приглашение на собеседование. Желанный оффер казался недосягаемым.</p><p>Ситуацию изменило менторство. Опытный бэкенд-разработчик и ментор <a href="https://t.me/sergey_filichkin_blog">Сергей Филичкин</a> подключился к процессу. Вместе они разобрали ошибки, скорректировали стратегию поиска, а главное — помогли Жене перестать воспринимать отказы как личное поражение.</p><p>Результат? После проработки всех проблемных моментов вместе с ментором всего спустя пару собеседований Женя получил первую вакансию Python-разработчика — именно ту, о которой мечтал: с понятными задачами и возможностью расти без выгорания.</p><p>Мы поговорили с Сергеем Филичкиным о том, как готовиться к собесам и искать работу, особенно когда от череды отказов хочется опустить руки.</p><h2>Как стать ментором: от сарафанного радио к полноценному проекту</h2><p>В IT я уже больше семи лет. Трудно точно посчитать, потому что начал ещё в университете подрабатывать: писал на разных языках, пробовал себя в разных проектах. Успел поработать и в маленьких стартапах с командой в несколько десятков человек, и в огромных корпорациях на десятки тысяч сотрудников. Благодаря этому я видел самые разные архитектуры и подходы, попробовал разные технологии и языки программирования.</p><p>Прошёл путь от новичка, который не понимает, куда двигаться, до работы на серьёзных позициях. Поэтому хорошо знаю, через что проходят джуны и какие трудности их ждут в самом начале.</p><p>Сначала я просто помогал знакомым: объяснял, что-то подсказывал. Потом ко мне начали обращаться и те, с кем я лично не был знаком. Полноценно, в публичном формате я занимаюсь менторством около полутора-двух лет. Начиналось всё довольно скромно: завёл соцсети, к нам приходило совсем немного учеников, и я работал с ними индивидуально.  Постепенно у меня родилась идея превратить это в <a href="https://www.sergeyfilichkin.ru/">продукт</a> — в полноценное менторство по трудоустройству Python-разработчиком.</p><p>Трудно выделить какой-то один путь развития ментора — это скорее черта личности. Мне всегда хотелось находить понятные алгоритмы: как работает рынок, на что смотрят рекрутеры, как правильно составить резюме. И потом постепенно улучшать эти алгоритмы. Это похоже на то, как пишешь код: сначала выстраиваешь базовую логику, потом шаг за шагом делаешь её лучше. В придачу помогает понимание того, что важно бизнесу и зачем тебя вообще нанимают. Когда есть осознание своей роли в системе, становится проще выстраивать весь процесс</p><p>Постепенно проект начал расти, появилась команда. Причём она у нас разноуровневая: есть люди, которые помогают выстраивать процессы, а есть те, кто работает напрямую с учениками. Я сам продолжаю участвовать во всех этапах, но теперь это уже системная работа.</p><p>Я увидел, что это работает, что людям действительно помогает. За это время мы довели до трудоустройства десятки ребят. И каждый день стараемся улучшать программу: добавляем новые форматы, тесты, домашние задания, вводим регламенты качества. Если вспомнить, с чего всё начиналось, то это был буквально чат в Telegram с небольшой подборкой тем. Сейчас же у нас полноценная платформа с уроками, практикой, проверками и системой поддержки.</p><p>У меня изначально был простой принцип: либо делаешь хорошо, либо не делаешь вовсе. Иначе смысла в этом нет. В самом начале я просто проверял гипотезу — есть ли вообще спрос на менторство. Оказалось, что интерес есть, и тогда я решил сконцентрироваться на этом направлении. Теперь все силы направляю на развитие проекта. Сделано уже много. Мы постоянно собираем обратную связь от учеников и дорабатываем программу.</p><p>У меня сейчас два основных направления работы — менторство для действующих разработчиков, которые хотят расти дальше по скиллам и уровню дохода; а также помощь в трудоустройстве на стартовые позиции для начинающих. Есть и отдельные форматы, например, разовая консультация или поддержка на испытательном сроке.</p><h2>Что происходит на рынке и почему Женя не мог найти работу</h2><p>Сейчас рынок действительно изменился. Сыграло несколько факторов одновременно:</p><ul><li>Кризис — и мировой, и локальный. Компании стали экономить, поэтому вакансий объективно стало меньше.</li><li>Кандидатов стало больше. Конкуренция выросла, и это естественно заставляет работодателей поднимать планку.</li><li>Чтобы выбрать из сотни резюме, компании добавляют дополнительные фильтры: усложняют задания, делают больше этапов, повышают требования на собеседованиях.</li></ul><p>Всё это логичная реакция рынка: меньше позиций, больше кандидатов, выше конкуренция — и, соответственно, жестче проверки.</p><p>В результате, рынок найма оказался в целом сломан. Нанимают как попало, часто задают стандартные вопросы из списков, которые легко нагуглить. Кандидаты, соответственно, тоже гуглят готовые ответы.</p><p>Рекрутеры зачастую не читают резюме внимательно — в HeadHunter нажимают пару фильтров и отсекают людей, которые могли бы подойти. У самих сотрудников, которые проводят собеседования, нет отдельной мотивации. Им просто говорят: «Иди проведи собес», они идут и делают, без особого энтузиазма.</p><p>Формально, конечно, все заинтересованы, чтобы пришёл сильный кандидат. Но на деле система работает не слишком надёжно, и поэтому мы видим, что процесс отбора часто выглядит формально и поверхностно.</p><p>Найти работу на нынешнем рынке стало сложнее, чем год-два назад. При этом немало соискателей низкого уровня — посмотрели ролик, сделали пару шагов и уже считают себя программистами. Таких становится всё больше.</p><p>Но даже в этих условиях устроиться реально. Если последовательно работать, использовать те инструменты, которые мы даём, и не останавливаться, результат будет. Да, стало тяжелее, но если есть цель, её можно достичь.</p><p>Женин пример это доказывает: кейс был непростым, но Женя сразу проявил себя как очень понимающий ученик. Он начал выполнять рекомендации буквально с первого дня: исправлял ошибки, подтягивал слабые места, советовался. Поэтому результат не заставил себя долго ждать.</p><p>До того, как взять помощь наставника Женя сам прорабатывал своё резюме около восьми часов, читал статьи на Хабре и Тпрогере, пытался применить описанные другими разработчиками лайфхаки. Но выходило слабовато — откликов на резюме практически не было.</p><figure><img src="https://media.tproger.ru/user-uploads/102593/2025-09-15/65709aa3-5b30-4c4c-9900-9548a6414b9a.png" alt="" /></figure><p>Мы несколько раз созванивались, обсуждали ключевые вещи — рынок, резюме, поведение на собеседованиях. Дальше я формировал для Жени план: какие шаги делать дальше.</p><p>Процесс выглядел так: он выполнял шаг — присылал результат. Это могло быть резюме, итоги собеседования или вопросы. Я давал обратную связь не в формате “ок/не ок”, а с конкретикой: что именно поправить — раз, два, три — и что делать следующим шагом. Если всё в порядке, фиксировали результат и переходили к следующему пункту. По сути, я выдавал ему алгоритм и сопровождал его шаг за шагом. После корректировки резюме Жене наконец стали поступать отклики с HH и приглашения на скрининги от HR.</p><p>Это, кстати, основная проблема многих новичков. Они не понимают, что именно нужно делать, или не следуют советам. Даже когда им говоришь: сделай раз, два, три — они откладывают или игнорируют. В итоге процесс затягивается. Я бы не сказал, что есть ситуации, где вообще ничего нельзя сделать. Единственный вариант — когда человек просто перестал учиться и не выходит на связь. Тогда, понятно, помочь невозможно. Но бывают тяжёлые случаи, когда ученик вроде бы делает то, что я говорю, но наполовину: указывает на ошибки, а он приходит через месяц с теми же самыми. И так повторяется раз за разом. Это сильно затягивает процесс. Но даже такие ребята в итоге устраиваются — просто путь у них дольше.</p><p>У Жени было иначе: он сразу включился и начал работать над собой. Поэтому и смог довольно быстро получить результат. Кейс, конечно, был не самый лёгкий, но по сравнению с ситуациями, где люди просто не выполняют рекомендации, он оказался рабочим и успешным.</p><p>Основная проблема была в том, что Женя не очень понимал, как устроен рынок: какое сейчас состояние, как правильно вести себя на собеседовании, что должно быть в резюме и как его составлять. Мы подробно разобрали все эти моменты. Плюс я указал темы, которые критически важно подтянуть. Это и стало ключевым — после доработок процесс пошёл заметно быстрее.</p><h2>Ошибки в резюме</h2><p>Резюме Жени было составлено без понимания рынка. Он просто добавил то, что посчитал важным, но информация оказалась нерелевантной для работодателей: формулировки, описание задач, достижений — почти всё выглядело не так, как нужно.</p><p>Здесь важно понимать: резюме сначала читает HR, а уже потом оно попадает к техническому специалисту. Поэтому нужен баланс. С одной стороны, должны быть ключевые технические слова, чтобы разработчик, проводящий собеседование, понял, что кандидат разбирается. С другой — HR нужно донести смысл простым языком, без перегрузки узкопрофильными аббревиатурами и сложными терминами.</p><p>Фронтенд, бэкенд — это понятно всем. Но когда начинаешь перечислять редкие технологии или паттерны проектирования, HR может не понять, и это нормально — он не обязан этого знать. Поэтому искусство резюме в том, чтобы объяснять простыми словами, но при этом сохранять технические ключи для специалистов.</p><p>Самостоятельно понять, что именно нужно рынке — непросто. Да, можно смотреть ролики на YouTube, читать статьи и искать материалы в открытом доступе, но чаще всего такие источники не заточены под реальный результат — трудоустройство.</p><p>Когда человек приходит к нам на программу менторства, мы заинтересованы в его результате и опираемся на большую статистику: что пробовали, что сработало, какие варианты дают отклик. Мы даём конкретные шаги, что именно делать.</p><p>Можно пытаться разбираться самому, но конкурировать с системой, где уже накоплены данные и опыт проб и ошибок, сложно. Проще обратиться за помощью — если есть желание и возможность.</p><p>Сроки трудоустройства зависят от конкретного человека. К нам приходят как совсем новички, которые ничего не знают, так и те, у кого уже есть опыт. Важно не только дойти до собеседования, но и правильно на нём отвечать, а потом ещё и закрепиться на работе. Наша цель — не просто довести человека до технического собеса, а реально изменить его жизнь: помочь устроиться и продолжать работать.</p><p>Были случаи, когда приходили люди вообще без знаний, даже без базового языка программирования. Мы вместе определяли, что именно учить, и они готовились. Да, это требует больше времени и усилий, но и такие ребята в итоге трудоустраиваются.</p><p>Самые быстрые кейсы: человек уже что-то умеет, но не понимает рынок и неправильно составил резюме. В таких случаях оффер может появиться буквально через две недели. Бывают и долгие истории — когда знаний нет вообще, и нужно много времени на изучение. Тогда процесс занимает месяцы, иногда больше полугода.</p><p>В среднем после окончания программы на поиск работы уходит около полутора месяцев. Кто-то быстрее, кто-то медленнее, но это наиболее частый срок.</p><p>Женя согласился, по сути, на первый оффер. Я понимаю решение Жени, но несколько раз говорил ему: это всего лишь один оффер, стоило бы поискать ещё, можно было претендовать на более высокую зарплату. Наши ученики устраиваются на позиции с гораздо лучшими условиями. Но это его жизнь, его выбор — он решил принять это предложение.</p><p>Думаю, результат говорит сам за себя: если раньше он долго не мог найти работу, а после советов устроился, значит, опыт был полезным.</p><p>Вообще понять, на какую зарплату соглашаться — не всегда непросто. Бизнесу выгодно нанять дешевле, кандидату — продаться дороже. Тут начинается игра. Есть негласное правило: кто первым назвал сумму — тот проиграл. Поэтому лучше пытаться узнать вилку заранее и называть цифру ближе к верхней границе. Если вилку никак не удаётся выяснить, ориентируйся на рыночные значения. Без понимания рынка сложно вообще адекватно оценивать оффер.</p><p>Да, тут есть риск: попросишь слишком много — могут отказаться. Но и занижать цену тоже опасно: компания подумает, что кандидат слабый, раз так дёшево себя оценивает. Но если оффер отозвали — ничего страшного, значит, это просто не твоя компания.</p><p>А если рекрутеры упорно не называют вилку, можно аккуратно перевести разговор: сказать, что ты рассматриваешь разные варианты, деньги для тебя важны, но это не ключевой фактор, и попробовать выудить условия уже от них. Главное — не продешевить и не соглашаться на меньшее только из-за страха упустить шанс.</p><h2>Кому нужен ментор и как прокачиваться</h2><p>Когда ты новичок, тебе сложно объективно оценить — у тебя крутое резюме или нет, хорошо ты прошёл собес или плохо. Просто нет опыта для сравнения.</p><p>Самый рабочий вариант — найти знакомого, который уже работает в этой сфере, и попросить его дать обратную связь. Если таких знакомых нет, можно искать профильные чаты и форумы: выложить туда резюме, попросить провести пробное интервью.</p><p>Но тут есть нюанс: гарантий качества нет. Может оказаться, что советы даёт такой же вчерашний новичок, который сам ещё мало что понимает. Поэтому лучше всё-таки найти более опытного специалиста и обратиться к нему напрямую. Это как с машинным маслом: если я в нём не разбираюсь, то сам не пойму, хорошее оно или плохое. Здесь ситуация точно такая же.</p><p>Аналогичная ситуация с подготовкой к собесам. На старте можно включить собеседования на YouTube по позиции, на которую ты претендуешь, и попробовать отвечать на вопросы. Если отвечаешь уверенно — значит, всё в порядке, если начинаешь путаться — это сигнал, что тему нужно подтянуть.</p><p>Дальше всё зависит от цели. Если задача — пройти собеседование, то стратегия одна: берёшь список из сотен популярных вопросов, прорабатываешь их. Но важно не просто заучивать готовые ответы, а понимать, почему именно так. Это сразу повышает уровень и как кандидата, и как специалиста.</p><p>Если цель — развиваться как разработчик, то лучшая практика — делать проекты. Причём не самые простые, а такие, где нужно использовать несколько современных технологий: не только язык программирования и фреймворк, но и дополнительные инструменты, подходы к построению систем, интеграцию разных решений. Такой опыт сильно ускоряет рост и реально прокачивает навыки.</p><p>Но навык прохождения собесов и устройства на работу — это отдельный скилл, который не зависит напрямую от твоих хардов. Ментор же помогает прокачаться именно с этой позиции. В нашем проекте мы всегда сначала пытаемся понять, с каким уровнем знаний к нам пришёл человек, чтобы выстроить индивидуальный подход. Но сама программа построена так, что её проходят все — и опытные, и новички. Разница лишь в скорости: если ты уже многое знаешь, идёшь быстрее, если нет — задерживаешься на сложных местах и разбираешь их глубже.</p><p>Программа включает тесты и практические задания, которые основаны на реальных кейсах разработки и вопросах с собеседований. С самого начала студенты начинают готовиться к реальным ситуациям. За каждым закрепляется личный наставник — человек, к которому можно обратиться за советом по любому вопросу.</p><p>После прохождения ключевых блоков идёт промежуточное собеседование: не по всем темам сразу, а по конкретному модулю. Оно помогает углубиться и закрепить знания. Темы в программе отобраны, мы не перегружаем лишним — только то, что реально востребовано на рынке.</p><p>Помимо этого, студенты попадают в сообщество: там есть уже трудоустроенные выпускники и те, кто ещё учится. Можно обмениваться опытом, задавать вопросы, получать поддержку. Еженедельно проходят групповые занятия и разборы, иногда приглашаем карьерных консультантов. Если активно участвовать во всём и выполнять задания, прогресс становится заметен очень быстро.</p><p>Ближе к завершению программы мы даём студентам практические инструменты для выхода на рынок: вместе составляем резюме, проверяем его в несколько этапов, разбираем ошибки. Точно так же прорабатываем весь процесс поиска работы — от откликов до собеседований. Также есть возможность участия в индивидуальных и групповых встречах с карьерными консультантами и действующими рекрутерами из различных компаний, в том числе и международных.</p><p>Но на этом всё не заканчивается. У нас есть отдельная услуга поддержки на испытательном сроке. Она появилась не случайно: многие ребята, уже получив оффер, начинают переживать — справятся ли они, пройдут ли испытание. Возникает масса вопросов не только технических, но и организационных, связанных с процессами внутри компании. Здесь менторы помогают снять лишнее напряжение и подсказать, как действовать.</p><p>Да, это платная услуга, но по сути её можно заменить и более простым способом — найти знакомого, который уже работает в IT и готов поддержать. Главное — иметь человека, у которого можно спросить совета, потому что на испытательном сроке часто мешают не сами задачи, а сомнения и стресс.</p><p>Наши менторы — это действующие разработчики, которые не только сами проходили этот путь, но и помогли десяткам учеников пройти его раньше. Поэтому мы накопили экспертизу и понимаем, с какими трудностями студенты сталкиваются в реальности.</p><p>Если вы тоже устали от поисков и пролистывания ленты HH, возможно, вам стоит обратиться к наставнику за помощью — тому, кто действительно понимает, что хотят видеть рекрутеры и как вам докрутить своё резюме, чтобы найти работу мечты.</p><p>А вы пользовались услугами менторов или наставников? Или помогали кому-то сами? Расскажите о своём опыте в комментариях.</p>]]></content:encoded>
    </item>
    <item>
      <title>Защита API-ключей: как избежать утечек</title>
      <link>https://tproger.ru/articles/zashhita-api-klyuchej--kak-izbezhat-utechek</link>
      <comments>https://tproger.ru/articles/zashhita-api-klyuchej--kak-izbezhat-utechek?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Денис Кудерин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/zashhita-api-klyuchej--kak-izbezhat-utechek</guid>
      <description><![CDATA[<p>Защита API-ключей. Показываем, как избежать утечек в API. Рассматриваем пошаговую инструкцию и инструменты ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/zashhita-api-klyuchej--kak-izbezhat-utechek">Защита API-ключей: как избежать утечек</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Яндекс]]></category>
      <category><![CDATA[Cloudflare]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Уязвимость]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 27 May 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>API-ключи стали цифровыми пропусками в современную веб-инфраструктуру. Они открывают доступ к данным, сервисам и функционалу, но их утечка превращает этот удобный механизм в угрозу безопасности.</p><p>Техническая уязвимость идентификаторов — не единственная проблема. Защита API-ключей требует комплексного подхода, сочетающего технические решения и организационные меры. Практика показывает, что большинство утечек происходит не из-за направленных атак, а по причине пренебрежения базовыми принципами безопасности.</p><p>Поэтому важно не просто правильно хранить ключи, но и контролировать их использование, своевременно обновлять и ограничивать область действия. Понимание этих рисков — первый шаг к созданию надежной защиты API для ваших интерфейсов и данных.</p><p>В этой статье разберем основные причины утечек, а также практические методы защиты API-ключей, которые помогут избежать распространенных ошибок и минимизировать риски.</p><h2>Основные причины утечек</h2><p>API-ключи обеспечивают доступ к критически важным системам — от платежных шлюзов до облачных хранилищ данных. Их компрометация может привести к катастрофическим последствиям — утечке конфиденциальных данных, финансовым потерям и даже к полному захвату контроля над системой.</p><p>Разберем основные причины утечек.</p><h3>Хардкодинг ключей в исходном коде</h3><p>Пожалуй, самая распространенная и опасная практика — прямое внедрение API-ключей в исходный код приложения. Разработчики часто делают это для удобства тестирования, забывая удалить секретные данные перед релизом.</p><p>Особенно критично, когда ключи остаются:</p><ul><li>в конфигурационных файлах (config.php, .env);</li><li>в закомментированных блоках кода;</li><li>в тестовых сборках, которые по ошибке попадают в продакшен;</li><li>в шаблонах и примерах кода.</li></ul><p>Яркий пример — <a href="https://xakep.ru/2023/09/07/microsoft-post-mortem/">инцидент с Microsoft в 2023</a> году, когда утечка ключа MSA (Microsoft Account) через устаревший код позволила хакерам получить доступ к почтовым ящикам правительственных организаций США, включая Министерство торговли. Как показало расследование Microsoft Security Response Center, проблема возникла из-за ключа подписи, который продолжал использоваться в legacy-системах и попал в руки злоумышленников.</p><p>Главная опасность хардкодинга в том, что даже после удаления ключа из актуальной версии кода он может сохраняться в истории версий или бинарных файлах. Современные сканеры секретов легко находят такие уязвимости, что делает подобную практику особенно рискованной.</p><h3>Случайные коммиты в публичные репозитории</h3><p>Системы контроля версий типа Git хранят полную историю изменений, что создает дополнительные риски. Даже если ключ удален из текущей версии кода, он может остаться в истории коммитов.</p><p>Типичные сценарии:</p><ul><li>временное добавление ключа для отладки с последующим «удалением»;</li><li>позднее добавление .env-файла в .gitignore;</li><li>слияние веток с чувствительными данными;</li><li>автоматические коммиты IDE и инструментов разработки.</li></ul><p>В публичные репозитории GitHub ежедневно попадают сотни активных ключей — некоторые из них даже предоставляют доступ к платежным системам и базам данных.</p><h3>Ошибки в настройке CI/CD</h3><p>Автоматизированные системы сборки и деплоя могут невольно способствовать утечкам:</p><ul><li>передача ключей через аргументы командной строки;</li><li>попадание секретов в логи сборки;</li><li>неправильная настройка переменных окружения;</li><li>хранение ключей в незащищенных артефактах;</li><li>избыточные права доступа для CI-сервисов</li></ul><p>Особенно опасны ситуации, когда CI-пайплайн настроен на публикацию артефактов сборки, включающих конфигурационные файлы с ключами.</p><h3>Логирование чувствительных данных</h3><p>Разработчики часто добавляют отладочный вывод, который затем забывают удалить:</p><ul><li>console.log (“API Key: “, secretKey);</li><li>погирование полных HTTP-запросов с заголовками авторизации;</li><li>дампы ошибок с конфиденциальными данными;</li><li>журналирование параметров запросов.</li></ul><p>Такие записи могут попасть в системные логи, инструменты мониторинга (Kibana, Grafana), браузерную консоль (для фронтенд-приложений) и облачные сервисы хранения логов.</p><p>Проблема усугубляется тем, что многие фреймворки по умолчанию включают подробное логирование, а разработчики не всегда задумываются о последствиях. В корпоративных системах это может привести к накоплению секретов в централизованных системах мониторинга, доступ к которым имеют десятки сотрудников.</p><h2>Неправильное управление доступами</h2><p>Часто проблема кроется не в технических решениях, а в процессах управления:</p><ul><li>отсутствие ротации ключей — некоторые из них используются годами;</li><li>использование одних ключей для разных сред (dev/stage/prod);</li><li>избыточные права доступа — принцип минимальных привилегий не соблюдается;</li><li>хранение ключей в общих хранилищах и чатах;</li><li>отсутствие аудита использования ключей.</li></ul><p>Часто утечки происходят из-за совокупности нескольких перечисленных факторов. Например, типичный сценарий: ключ сначала попадает в код, затем в репозиторий, обнаруживается в логах CI-системы, а потом оказывается в общем доступе из-за неправильных настроек прав.</p><p>Особую опасность представляет практика использования долгоживущих ключей с широкими правами доступа. В отличие от временных токенов, такие ключи редко проверяются и могут годами оставаться незамеченными в случае утечки. Поэтому современные подходы к безопасности рекомендуют использовать краткосрочные реквизиты для входа с минимально необходимыми правами.</p><h2>Безопасное хранение и использование API-ключей</h2><p>API-ключи — это критически важные элементы инфраструктуры, и их утечка недопустима. Чтобы минимизировать риски, важно соблюдать несколько принципов.</p><p>Переменные окружения — один из базовых, но эффективных способов изоляции ключей от кода. Хранение их прямо в скриптах или конфигурационных файлах, особенно в публичных репозиториях, — распространенная ошибка.</p><p>Сканер секретов GitHub ежедневно обнаруживает сотни случайно залитых ключей, несмотря на предупреждения. Переменные окружения позволяют отделить конфиденциальные данные от кода, но важно убедиться, что файлы .env не попадают в билды или логи.</p><p>Для более сложных сценариев стоит рассмотреть специализированные хранилища секретов, такие как HashiCorp Vault, AWS Secrets Manager или Doppler. Они не только обеспечивают безопасное хранение, но и добавляют функции ротации ключей, аудита доступа и интеграции с системами мониторинга.</p><ul><li><b>Vault</b> динамически генерирует временные ключи для отдельных сервисов, сводя к нулю риск их повторного использования. Однако такие решения требуют настройки и контроля — при некомпетентном подходе компании сталкиваются с ошибками конфигурации при внедрении.</li><li><b>AWS Secrets Manager</b> — инструмент, который позволяет централизованно хранить конфиденциальную информацию, извлекать ее, управлять доступом, ротировать и мониторить.</li><li><b>Doppler</b> — кроссплатформенное решение с удобным интерфейсом и историей изменений. Особенно популярно среди стартапов.</li></ul><p>Главное преимущество таких систем — централизованное управление. При увольнении сотрудника или компрометации ключа его можно отозвать мгновенно для всех сервисов.</p><p>Шифрование обязательно как при передаче, так и при хранении. Даже если злоумышленник получит доступ к базе данных или логам, зашифрованные ключи останутся бесполезными без расшифровки. Современные стандарты, такие как AES-256 или алгоритмы на основе PQC (постквантовой криптографии), уже встроены в большинство облачных провайдеров. Но важно не забывать про управление ключами шифрования (KMS): их утечка сведет на нет всю защиту.</p><p>Ограничение доступа — еще один уровень безопасности. Даже корректно хранимый ключ должен работать только с определенных IP-адресов, в заданные промежутки времени и для конкретных методов API. Например, ключ для чтения данных не должен разрешать запись. Cloudflare <a href="https://blog.cloudflare.com/">рекомендует</a> комбинировать геофильтрацию, ограничение частоты запросов и сигнатурный анализ запросов для блокировки аномальных действий.</p><p>Наконец, мониторинг помогает обнаружить утечку до того, как ею воспользуются. Инструменты вроде AWS GuardDuty или открытый вариант Falco отслеживают подозрительные операции: неожиданные запросы из новых регионов, аномальную частоту вызовов API или попытки доступа к заблокированным эндпоинтам.</p><p>Важно: ни один метод не дает 100% защиты API. Нужно комбинировать подходы и регулярно аудировать систему. Безопасность API-ключей — это не разовая настройка, а процесс, требующий регулярного пересмотра политики адаптации к новым угрозам.</p><h3>Лучшая практика работы с ключами</h3><p>Хранение API-ключей требует особого внимания, так как их компрометация может привести к серьезным последствиям. Около половины всех утечек ключей происходят из-за их хардкодирования в исходном коде. Это базовая ошибка, которую легко избежать, используя переменные окружения или специализированные хранилища секретов.</p><p>Вот самые эффективные практики:</p><ul><li>Первое правило — никогда не оставлять ключи в коде. Даже если репозиторий приватный, всегда существует риск случайной публикации или утечки через резервные копии. Инструменты вроде pre-commit хуков помогают предотвратить подобные инциденты, автоматически проверяя изменения перед отправкой. Например, скрипт может сканировать коммиты на наличие строк, похожих на ключи, и блокировать их сохранение.</li><li>Регулярная ротация снижает потенциальный ущерб от возможной компрометации. Ключи, которые не обновлялись годами, представляют особую опасность. Современные системы, такие как HashiCorp Vault или AWS Secrets Manager, позволяют автоматизировать этот процесс.</li><li>Принцип минимальных привилегий должен применяться ко всем ключам. Если токен нужен только для чтения данных, он не должен иметь прав на запись или удаление. Ограничение области действия каждого токена — простой, но эффективный способ снизить риски.</li><li>Мониторинг использования помогает выявлять аномалии в реальном времени. Неожиданные всплески активности, запросы из новых регионов или попытки доступа к неиспользуемым методам API — все это сигналы потенциальной компрометации. Инструменты типа AWS CloudTrail или Elastic SIEM позволяют отслеживать подобные события и оперативно реагировать на угрозы.</li><li>Обучение команды не менее важно, чем технические меры. Регулярные тренинги и чек-листы помогают поддерживать уровень осведомленности.</li></ul><p>Централизованное управление упрощает контроль за ключами. Когда все токены хранятся в одном защищенном месте, проще отслеживать их использование, вовремя обновлять и отзывать при необходимости. Это также облегчает аудит, который часто требуется для соответствия стандартам вроде PCI DSS или ГОСТ Р 56939-2024.</p><p>Процесс отзыва должен быть максимально оперативным. В случае компрометации ключа важно не только сгенерировать новый, но и убедиться, что старый уже недействителен. Некоторые сервисы, например Google Cloud, позволяют автоматически блокировать ключи при обнаружении подозрительной активности.</p><h3>Автоматическое выявление утечек</h3><p>Обнаружение утекших API-ключей должно быть неотъемлемой частью стратегии безопасности. Современные инструменты позволяют выявлять компрометацию секретов на ранних стадиях, минимизируя потенциальный ущерб.</p><p>Эффективный мониторинг начинается с проверки исходного кода. Такие инструменты, как <a href="https://www.gitguardian.com/">GitGuardian</a> и <a href="https://trufflesecurity.com/">TruffleHog</a>, сканируют git-репозитории, включая историю коммитов, на наличие случайно оставленных ключей. Они используют комбинацию шаблонов и энтропийного анализа, что помогает находить даже замаскированные секреты. Интеграция этих проверок в CI/CD-пайплайны позволяет перехватывать потенциальные утечки до их попадания в основную ветку.</p><p>Для обнаружения ключей за пределами репозиториев существуют специализированные сервисы, которые мониторят открытые площадки вроде Pastebin и технических форумов. Некоторые решения способны анализировать контекст, отличая реальные ключи от случайных последовательностей символов.</p><p>При выявлении компрометации критически важна оперативная реакция. Первый шаг — немедленный отзыв скомпрометированного ключа. Современные системы управления секретами позволяют делать это автоматически, одновременно инициируя процесс генерации замены. Задержки в этом процессе создают опасное окно уязвимости.</p><p>Проактивный мониторинг должен сопровождаться четким планом реагирования. Документированные процедуры позволяют сократить время реакции и минимизировать последствия инцидента. Важно регулярно тестировать эти процедуры на практике.</p><p>Автоматизированные системы обнаружения утечек работают наиболее эффективно в сочетании с обучением разработчиков. Технические средства бесполезны, если команда продолжает пренебрегать базовыми правилами безопасности. Регулярные тренировки помогают поддерживать высокий уровень готовности к реальным угрозам.</p><p>Важно понимать, что автоматическое обнаружение — это последний рубеж защиты. Оно не заменяет, а дополняет другие меры безопасности, такие как грамотное хранение ключей и контроль доступа. Комплексный подход значительно снижает риски, связанные с компрометацией API-ключей.</p><h2>Как реализовать безопасный доступ на фронтенде</h2><p>Основная проблема фронтенд-разработки в контексте работы с API — невозможность полностью защитить клиентский код. В отличие от серверной части, JavaScript остается открытым для анализа, а сетевые запросы легко перехватываются. Однако существуют проверенные подходы, позволяющие минимизировать риски утечки ключей.</p><p>Первое и главное правило — никогда не хранить приватные ключи в клиентском коде. Даже минифицированный JavaScript легко декомпилируется, а строковые константы извлекаются за несколько минут с помощью стандартных инструментов разработчика.</p><p>Вместо этого рекомендуется использовать архитектурный паттерн Backend-for-Frontend (BFF), когда фронтенд общается с промежуточным сервером, а тот уже взаимодействует с основными API. Так ключи остаются на защищенной стороне, а клиент получает только временные токены с ограниченным сроком действия.</p><p>Для аутентификации пользователей оптимально подходит OAuth 2.0 с расширением PKCE (Proof Key for Code Exchange). Этот механизм обеспечивает безопасный обмен токенами даже в публичных клиентах. В отличие от обычного потока авторизации, PKCE добавляет дополнительный уровень защиты через одноразовые ключи верификации, что предотвращает перехват кодов злоумышленниками.</p><p>В случаях, когда фронтенду необходим доступ к публичным API без аутентификации пользователя, стоит использовать ограниченные токены. Например, для картографического сервиса можно выпускать ключи, разрешающие только чтение данных с жестким лимитом запросов. Такой подход применяют многие крупные платформы, включая Яндекс.Карты. Даже если токен будет скомпрометирован, его полезность для злоумышленников окажется минимальной.</p><p>Дополнительную безопасность обеспечивает динамическое получение ключей. В этой схеме фронтенд сначала запрашивает у сервера одноразовый код, затем использует его для подписи запроса. После проверки подписи сервер выдает временный ключ, действительный только для текущей сессии. Этот метод значительно усложняет массовый перехват и повторное использование украденных данных.</p><p>Фронтенд должен получать ровно столько данных, сколько необходимо для отображения интерфейса, а все критические операции должны выполняться на сервере. Комбинация прокси-серверов, временных токенов и строгого контроля доступа позволяет создать надежную систему защиты даже для чувствительных API.</p><p>Последний рубеж безопасности — верификация среды выполнения. Современные библиотеки анализируют параметры браузера, выявляя признаки эмуляции или автоматизации. Это позволяет блокировать запросы от ботов и скриптов, пытающихся массово собирать данные через API. Такой подход особенно важен для финансовых сервисов и платформ с ценной информацией.</p><p>Главный вывод: фронтенд действительно представляет угрозу для безопасности API-ключей, но грамотная архитектура и продуманные механизмы авторизации позволяют снизить риски до приемлемого уровня. Ключевой принцип защиты API — минимализм в правах доступа и максимальное делегирование ответственности серверной части.</p><p>Хочешь писать код, который не стыдно показывать? Всё для фронтендеров и бэкендеров в <a href="https://t.me/+c6lPaQBXLvE4YmMy">одном месте</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Кому и зачем нужны профессиональные сообщества</title>
      <link>https://tproger.ru/articles/professionalnye-soobshchestva-erid-ljn8jwaag</link>
      <comments>https://tproger.ru/articles/professionalnye-soobshchestva-erid-ljn8jwaag?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Екатерина Соколова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/professionalnye-soobshchestva-erid-ljn8jwaag</guid>
      <description><![CDATA[<p>Разобрали, что такое профессиональные сообщества в Росбанке и зачем их развивают, и ответили на вопрос, почему стоит быть частью такого комьюнити.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/professionalnye-soobshchestva-erid-ljn8jwaag">Кому и зачем нужны профессиональные сообщества</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Карьера]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 13 Jun 2024 07:57:40 GMT</pubDate>
      <content:encoded><![CDATA[<p>Профессиональные сообщества — это и отдельные структуры в компаниях, и кружки по интересам, и группа людей, которая пришла на конференцию или IT-пикник. Даже Tproger, Хабр и GitHub в какой-то мере являются профессиональными сообществами, просто очень большими и распределенными. На каждой площадке говорят, что нужно создавать и развивать профессиональные сообщества, и почти любой айтишник должен вступить хотя бы в одно. Почему? И какую реальную пользу комьюнити приносят участникам и создателям? Разобрали на трех гильдиях команды Росбанка.</p><h2>Профессиональное сообщество — место, где говорят о планах и изменениях и находят решения для бизнеса</h2><p>Я организовал и лидирую гильдию архитекторов, разных специалистов, которые отвечают не только за информационные системы, но и за железо, и за безопасность, за аналитику.</p><p>Изначально гильдия задумывалась, как сообщество людей, которые занимаются схожими задачами, которым нужны схожие профессиональные навыки. И понимание того, что происходит в компании, как меняется IT-система и какое влияние это оказывает на команды — более глубокое, чем можно дать во внутренней базе знаний или на официальной встрече.</p><p>Решил, что давать информацию лучше на открытых митапах. Потому что в условном Telegram-чате или канале неудобно обсуждать большие и сложные вопросы (особенно те, что касаются не одной команды, а десятков специалистов из разных подразделений), да и нужно личное общение. А обязательные рабочие встречи малоэффективны: когда принуждаешь людей слушать что-то, информацию усваивают гораздо хуже, потому что меньше мотивации, и обратная связь будет куда менее полезной.</p><h3>Как это выглядит</h3><p>Участники гильдии предлагают тему, готовят доклад, возможно — презентацию, и гильдия раз в месяц организует выступление. Темы обычно делятся на три группы:</p><ul><li>важные изменения, которые происходят в IT-ландшафте банка: в чем их суть, какие у них цели, как это повлияет на сотрудников:</li><li>текущие инструменты и стандарты: чем стоит пользоваться, а чем — нет;</li><li>инновации: что новые архитекторы разных команд пробовали внедрить в последнее время и какие результаты получили.</li></ul><p>Прийти на митап может любой желающий, вне зависимости от направления, не обязательно архитектор.</p><p>Митапы проходят в рабочее время, а не по вечерам и в выходные, поэтому мы стараемся общаться с руководителями и объяснять им, что ходить на такие встречи важно — не только для архитектора, но и для продукта.</p><p>В итоге гильдия стала сообществом, где можно узнать информацию о планах развития наших информационных систем, профессиональных трендах, внутренних стандартах и опыте коллег, которая в таком подробном виде не публикуется больше нигде. Это позволяет архитекторам (и не только) быстрее и успешнее выполнять задачи, справляться с трудностями, погружаться в работу банка (что важно новичкам). А еще сообщество позволяет людям найти друг друга: коллег со схожими интересами, задачами и проблемами, что особенно актуально при удаленном формате работы.</p><p>А я дополнительно получаю опыт выстраивания горизонтальных рабочих процессов с большим количеством людей.</p><p>Мы смотрим, сколько людей ходит на встречи, собираем обратную связь. И формат действительно оказался эффективным. Количество постоянных и активных участников растет: люди возвращаются на новые митапы, присоединяются к Telegram-каналу (да, мы сделали его, чтобы синхронизироваться, анонсировать встречи, разбирать мелкие вопросы), чаще обсуждают рабочие вопросы и задачи, разворачивают дискуссии. И, судя по отзывам, архитекторы уже привыкли узнавать об изменениях в профессии и банке, решениях руководства и регуляторов из такого источника.</p><h2>Профессиональное сообщество — место, где говорят о технологиях и учатся выступать</h2><p>Лет пять назад, когда я пришел фронтенд-разработчиком в Росбанк, я столкнулся с трудностями на одном из проектов. Понятного решения нигде не нашлось, спросить было не у кого, и на проработку одной проблемы ушло три месяца. Мне показалось, что это неэффективно, и что явно на новых проектах тоже будут появляться вопросы, поэтому необходимо пространство, где команда будет обмениваться опытом и помогать с задачами. Никакого чата для этого в компании не было, поэтому я решил создать его сам. Организовал, собрал в нем примерно 300 человек. В то время в компании как раз зарождались гильдии по компетенциям. Поэтому мне заодно предложили возглавить гильдию по моему направлению — гильдию фронтенда.</p><p>Казалось бы, для ответов на сложные вопросы существует документация. Но читать её, даже если очень надо, никто не любит. К тому же есть команды с одним фронтендером, которому просто не у кого и негде спросить что-то по задаче.</p><h3>Организовал регулярные митапы, где раз в две недели говорим о технологиях, разбираем кейсы</h3><p>Например, недавно обсуждали новую версию Angular, фреймворк, о котором подготовил доклад один из наших участников, говорили о подходе к построению архитектуры и коммуникации между фронтом и бэком. На один митап приходится один доклад: фронтендеры сами приходят с идеями (строго плана у нас нет), иногда что-то интересное подкидываем мы с деврелами.</p><p>Время от времени мы просто собираемся поболтать на тему фронтенда, не привязываясь к докладам. Или устраиваем конференции за пределами банка, например, недавно состоялся митап в Казани.</p><figure><img src="https://media.tproger.ru/user-uploads/75384/2024-06-13/bc42d84e-3c5d-41f0-b2e6-fd0a2910c6b7.jpg" alt="" /></figure><p>Еще запустили Telegram-канал, рассылку и внутренний портал сообщества, через которые привлекаем людей и анонсируем встречи. Также развиваем сторонние проекты:</p><ul><li>карту компетенций, которая помогает каждому разработчику разобраться, какие навыки ему следует развивать в ближайшей перспективе;</li><li>базу знаний с записями митапов и полезными материалами.</li></ul><p>В планах — запустить внешнее и внутреннее обучение по фронтенду (платформа у компании есть, осталось создать курсы), привлекать на выступления известных разработчиков и сделать проект для обучения разработчиков.</p><h3>Почему это классно?</h3><p>Потому что ты с гильдией, ты всегда знаешь, куда идти за ответами или помощью в задачах. К тому же люди начинают узнавать друг о друге, разговаривать друг с другом (что особенно важно для маленьких команд, которые перестают «вариться» в своей среде и начинают чувствовать себя частью чего-то большего). Бизнесу это тоже полезно: разработчики делятся опытом друг с другом, меньше обращаются к начальству по мелким техническим вопросам — и продукты создаются быстрее.</p><p>Мне кажется, самую большую пользу получают докладчики. Потому что они учатся выступать на большую аудиторию. И когда ты готовишь доклад, перебираешь очень много информации — и многое изучаешь сам.</p><p>Я как лидер встречаюсь с огромным количеством людей, закрываю свои задачи и нахожу новые проекты. Например, недавно мне нужно было найти сильного разработчика в команду, я пошел по знакомым из гильдии, взял у них несколько контактов — и быстро подобрал классного специалиста. Еще предложили сделать HR-бота, помощника по онбордингу — это классная идея, не знаю, пришли бы ко мне ней, если бы не гильдия. Наконец, если у меня возникает какой-то вопрос, я просто иду за помощью в гильдию.</p><h2>Профессиональное сообщество — полноценная школа для специалистов и менеджеров</h2><p>В компании не было центра компетенций, который вырабатывает шаблоны, формирует стандарты и всем аналитикам компании рассказывает, как работать правильно.</p><p>Сперва я попробовал исправить это с помощью вебинара: собрал аналитиков, рассказал, как стоит писать требования, какие инструменты и приемы можно использовать, чтобы получить максимальный профит и согласовывать документы быстрее. После него на меня вышел начальник управления и предложил поучаствовать в создании сообщества. Я согласился, потому что решил, что это будет классным пет-проектом, постепенно брал на себя все больше ответственности — и дорос до лидера гильдии.</p><h3>Изначально мы создавали что-то вроде кружка по интересам</h3><p>Где будем общаться, проводить митапы, делиться опытом. Но не хватало четкой ценности: а делать сообщество, просто чтобы было, идея сомнительная. Я предложил сформулировать эти ценности, миссию и на их основе и проработать стратегию на 2024 год.</p><p>Видение звучит так: «Гильдия — это профессионалы, которые владеют лучшими, современными практиками в бизнес- и системном анализе». Миссия — «Создать сообщество таких профессионалов, дать площадку для обмена знаниями».</p><p>По моему опыту, в компаниях команды работают по-разному: у аналитиков свои подходы и инструменты, кто-то сильнее, кто-то слабее. И гильдия призвана собрать этих людей вместе и выровнять подходы: отобрать лучшие стандарты, подтянуть всех до одинаково высокого уровня.</p><p>В рамках этой стратегии мы начали проводить внутренние митапы и мастер-классы, аналитические посиделки (с дебатами, играми и общением с аналитиками из других компаний), создали школы системного и бизнес-анализа, разработали матрицу компетенций, составили список инструментов, которые рекомендуем использовать в работе. А одна из наших участниц даже сделала трехступенчатый курс по машинному обучению для МИФИ.</p><p>Кстати, на создание гильдии и всех проектов ушло меньше года.</p><figure><img src="https://media.tproger.ru/user-uploads/75384/2024-06-13/1c5e9116-b6c6-43cd-b9a4-470a9b7b5542.jpg" alt="" /></figure><h3>У нас даже выстроилась очередь из желающих выступить на митапе</h3><p>Мы планировали проводить один митап в два месяца, но благодаря деврелу у нас собралось очень много спикеров, и теперь мы в среднем проводим два митапа в месяц. Даже приходится бронировать места и записывать, кто за кем выступает. Темы разные: от профессиональных до научпопа.</p><ul><li>Недавно мы обсуждали «Баги мозга» — одна из наших сотрудниц рассказывала про Эффект Манделы на примере волка из мультика «Жил-был Пес». Оказалось, что он не говорил знаменитое «Ты это… заходи, если что…».</li><li>Одна из последних хардовых тем — визуализация архитектуры. Когда архитектор при проектировании делает визуал, и ты, грубо говоря, кликая на сервис, спускаешься на подсервисы — вплоть до кода.</li></ul><ul><li>Есть и доклады о конкретных кейсах, например, запуске кредитного калькулятора.</li></ul><p>Каких-то строгих критериев отбора тем у нас нет. Разве что, помогаем докрутить тему и сделать презентацию. Единственное — хочется слушать что-то небанальное и незаезженное, вроде «Как писать требования к продукту».</p><h3>Отдельно разберу школы на примере бизнес-анализа, потому что сейчас я занимаюсь ей</h3><p>Индекс HH показывает, что на рынке аналитиков отношение резюме к вакансиям составляет 0,8. То есть аналитиков меньше, чем мест работы, и подобрать сотрудника в команду очень сложно, приходится бороться за специалистов. Решение этой проблемы — закрывать вакантные места уже имеющимися кадрами, которые хоть и не имеют релевантного опыта, но готовы учиться и лояльны Росбанку.</p><p>Мы создали школу:</p><ul><li>Сформировали программу: восьминедельное обучение основам бизнес-анализа (BPMN 2.0, UML, общение со стейкхолдерами и так далее) с лекциями раз в неделю и обязательными домашними заданиями, которые делаем на базе реальных задач и процессов из нашего Confluence. За основу брали информацию из нашей внутренней базу знаний.</li><li>Открыли набор и получили 310 заявок, из которых отобрали 50 на первый поток и 60 на второй. При отборе смотрели на бэкграунд и мотивацию: почему люди хотят попасть в школу и готовы ли они реально учиться по пять часов в неделю, а не просто слушать лекции и пить в перерывах кофе.</li><li>Закончили первый поток и в июне запустим второй.</li></ul><p>Разумеется, мы собираем обратную связь и измеряем степень удовлетворенности участников. Минимальная планка — 75%, но мы добрались до 89% и услышали очень много приятных и лестных слов о школе.</p><h3>И расскажу немного про пользу, которую получаем от гильдии</h3><p>Для участников это, в первую очередь — команда людей с такими же профессиональными интересами и задачами. Особенно это важно на старте работы: поначалу в банке ты чувствуешь себя потеряшкой, не понимаешь, как тут принято работать и общаться, и хочешь быстрее интегрироваться в задачи и культуру.</p><p>Да и ощущение того, что ты не одинок, и вокруг 300–350 таких же аналитиков, которые готовы помочь, поделиться идеям и наработками, очень важно.</p><p>А для меня это пет-проект. Я занимаюсь менеджментом, только что закончил двухлетнюю программу МВА по этому направлению, хочу прокачивать хард и софт-скилы на практике, а не только по учебникам.</p><p>И для меня гильдия, не просто сообщество, а сообщество людей, которых нужно вдохновлять, мотивировать не только деньгами. Мне интересно заниматься именно этим: искать энтузиастов и лидеров, привлекать людей, которым интересно делать что-то классное не только ради бонуса, а чтобы поделиться этим с окружающими. И я сам хочу делиться чем-то классным.</p><h2>Как получить пользу от профессионального сообщества</h2><p>В конце подведем итог: что нужно делать, чтобы получить реальную пользу, а не просто ходить по каким-то встречам.</p><ul><li>Организовать сообщество самостоятельно, если его еще нет в компании. Это прокачает и технические, и менеджерские навыки, поможет найти классных людей в команду и новые интересные проекты.</li><li>Не относиться к участию, как к обязанности: так, скорее всего, потеряется мотивация.</li><li>Выступать: когда чему-то учишь других, лучше всего учишься сам.</li></ul><p>Для тех, кто готов строить крутую карьеру в IT, IT-команда Росбанка ведёт свой <a href="https://tprg.ru/JQT6">Telegram-канал</a>. Подписывайтесь и будьте в курсе актуальных вакансий компании и анонсов важных событий для профессионалов в IT.</p><p><i>Реклама, ПАО «Росбанк», erid: LjN8JwaaG</i></p>]]></content:encoded>
    </item>
    <item>
      <title>10+ методов от сеньоров и тимлидов для слаженной работы с командой и карьерного роста</title>
      <link>https://tproger.ru/articles/10-metodov-ot-senorov-i-timlidov-dlya-slazhennoj-raboty-s-komandoj-i-karernogo-rosta-erid-ljn8jtbn9</link>
      <comments>https://tproger.ru/articles/10-metodov-ot-senorov-i-timlidov-dlya-slazhennoj-raboty-s-komandoj-i-karernogo-rosta-erid-ljn8jtbn9?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вика Овсянникова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/10-metodov-ot-senorov-i-timlidov-dlya-slazhennoj-raboty-s-komandoj-i-karernogo-rosta-erid-ljn8jtbn9</guid>
      <description><![CDATA[<p>Составили лонгрид про общение в команде: как проводить созвоны, организовывать брейнштормы, корректно давать фидбек или решать конфликты.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/10-metodov-ot-senorov-i-timlidov-dlya-slazhennoj-raboty-s-komandoj-i-karernogo-rosta-erid-ljn8jtbn9">10+ методов от сеньоров и тимлидов для слаженной работы с командой и карьерного роста</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Для мотивации]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Карьера]]></category>
      <category><![CDATA[Управление проектами]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Для продвинутых]]></category>
      <category><![CDATA[Soft Skills]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 18 Mar 2024 16:32:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вы джун+ или мидл разработчик. Вроде харды на высоком уровне, но что-то все равно не позволяет вам сменить грейд. Обычно это софт-скилы — на более высоких позициях вы не просто выполняете задачи, но еще и ставите их другим, а также направляете и учите младших коллег. Чтобы перейти на следующую карьерную ступень, придется подтягивать и мягкие навыки.</p><p>Мы пообщались с сотрудниками и экспертами сопровождения <a href="https://tprg.ru/NKOD">Яндекс Практикума</a> и составили обширный лонгрид, в котором вы найдете фишки для выстраивания эффективного и живого общения с командой.</p><h2>Оглавление</h2><ol><li><a href="https://tproger.ru/#one">Почему важно живое общение</a></li><li><a href="https://tproger.ru/#2">Как организовывать созвоны, от групповых до 1-2-1</a></li><li><a href="https://tproger.ru/#3">Как готовиться и проводить групповые созвоны</a></li><li><a href="https://tproger.ru/#4">Что делать с 1-2-1</a></li><li><a href="https://tproger.ru/#5">Как давать обратную связь</a></li><li><a href="https://tproger.ru/#6">Как разрешать конфликты</a></li><li><a href="https://tproger.ru/#7">Как проводить брейнштормы</a></li><li><a href="https://tproger.ru/#8">Как организовать трекер</a></li><li><a href="https://tproger.ru/#9">Как организовать общение в течение дня</a></li><li><a href="https://tproger.ru/#10">Как в развитии софтов может помочь школа Яндекс Практикума</a></li></ol><h2>Почему важно живое общение</h2><p>Под живым общением мы подразумеваем живую коммуникацию, при которой команда активно вовлекается в работу, а не сухо отвечает «Ок» или вообще молчит. Выстроить работу без этого сложно. Команда — это люди с общими целями, но у каждого участника есть своя идентичность. Живое общение помогает:</p><ul><li>сохранить связь с реальностью,</li><li>эффективнее креативить и решать сложные проблемы,</li><li>создавать чувство безопасности.</li></ul><blockquote>У разработчиков, людей технического склада ума, есть страх, непонимание: я вроде бы прочитал, что мне написали, но не понял, что мне хотели сказать просто из-за того, что это текст. Я часто сталкивалась с бэкенд-разработчиками или девопсами, которым сложно даже эмоджи считывать. Они говорят: а я не знаю, он улыбнулся, потому что подшучивает надо мной или потому что помочь хочет.</blockquote><p>Мидлам чаще приходится применять софт-скилы: помогать, давать обратную связь джунам, проводить встречи и т. д. Развитое живое общение поможет наладить с коллегами понимание и потенциально вырасти в должности — сеньоры пользуются софт-скилами чуть ли не чаще, чем хардами. И помочь в этом может Школа наставников Яндекс Практикума.</p><h2>Как организовывать созвоны, от групповых до 1-2-1</h2><p><b>Самые ходовые инструменты:</b> Discord, Zoom, Microsoft.Teams, Google.Meet</p><p>Можно использовать Discord как инструмент живого взаимодействия. Он создает эффект опенспейса на удаленке: там можно разбиться по комнатам, постоянно кто-то онлайн, занимается парным программированием. Такая атмосфера работает сама на себя: ты видишь, что в комнате есть люди, происходит движ, к ним можно быстро зайти и уточнить какой-то вопрос. Discord хорош для формата ежедневных коммуникаций в маленьких командах разработки на 6-8 человек.</p><p>В Zoom для этого банально нужно больше кликов и времени: создать встречу, отправить приглашения, зайти в приложение. Но он хорошо подходит для формата вебинаров, докладов, где разговаривает в основном один человек.</p><p>У нас распределенная команда, все работают удаленно из разных точек России, поэтому я старался делать дополнительные слоты, в которые мы могли бы собраться и во что-то позалипать. Мы играли в онлайн-версию испорченного телефона: дорисовывали картинки друг за другом и дописывали истории, чтобы сплотить ребят. Для этого тоже использовали Discord.</p><p>В Zoom или Meet есть свои плюсы — можно записать митинги. Но есть и минусы: если кто-то что-то обсуждает, ты не можешь к ним как в офисе подойти и послушать, что они говорят. А Discord как раз это решает.</p><h3>Как готовиться и проводить групповые созвоны</h3><p>Для начала нужно объяснить, зачем человеку приходить на встречу. Сформируйте и зафиксируйте в описании цель встречи, адженду, тщательно продумайте состав участников: точно ли вам нужна вся команда?</p><p>Дальше смотрим на контекст встречи. К каким-то можно не готовиться, например, там, где нужно просто сделать объявление. Кстати, новости лучше рассказывать лично, а не в переписке, чтобы обсудить возникающие вопросы и сразу получить обратную связь.</p><h4>Собирайте ожидания от встречи</h4><p>Иногда даже при прописанной адженде люди могут ожидать не того, о чем собирался говорить фасилитатор. Уточните перед началом, что все понимают, что вы будете обсуждать. Это полезная обратная связь: если рассинхронились, то можно либо на ходу менять тему, либо сразу акцентировать, что это не то место. В дальнейшем вы, как организатор, поймете, как формулировать приглашение на встречу так, чтобы не дезинформировать людей.</p><h4>Проводите разминки</h4><p>Во время созвона для начала нужно расшевелить аудиторию. Представьте, встречается толпа незнакомцев. Чтобы создать комфортную атмосферу, нужно их расслабить, дать познакомиться. Можно начать с неформального вопроса в духе «Как я провел выходные». Так происходит очеловечивание квадратиков на экране. Мы же обычные люди, а не просто скучные специалисты, у нас есть прикольные интересы, которыми можно поделиться. Если в компании есть культура включать камеры, то вообще прекрасно, потому что так гораздо проще устанавливать контакт.</p><h4>Контролируйте общий план созвона, чтобы не отклоняться от курса</h4><p>Участники часто начинают углубляться в детали, до которых проект, возможно, даже не дошел. Надо честно говорить: коллеги, кажется, мы общаемся не по теме. Это самая действенная штука, я не знаю лучше варианта.</p><h4>В такие моменты важно говорить уверенно: да, было!</h4><p>На ретроспективе иногда обсуждают вопросы, которые одной команде могут быть неинтересны: больше связаны с тестированием или с разработкой. Тогда участники начинают скучать. Чтобы вернуть их, можно еще спросить про опыт других команд: а как было у вас? Даже если эта тема им не сильно близка, это поможет включиться в обсуждение.</p><h4>Поддерживайте динамику созвона с помощью теории вдоха-выдоха</h4><p>Вне зависимости от формата, будь то рабочая встреча, вебинар, лайв-кодинг, или что-то еще, очень важна динамика встречи и теория вдоха-выдоха. Это про когнитивную нагрузку. Человек не может сконцентрировано воспринимать информацию слишком долго, мозгу нужно отдыхать. Поэтому лучше чередовать «вдохи» и «выдохи».</p><p>Вдох — это концентрация. Мы напрягаемся, идет когнитивная нагрузка, воспринимаем информацию. Выдох — мы переключаемся и даем мозгу передохнуть. Пусть это будут хотя бы несколько коротких выдохов даже в рамках очень серьезной встречи. Выдохами могут быть разные вещи, в зависимости от формата: тот же мемчик в презентации, если мы понимаем, что был долгий и сложный блок. Или обсуждение вопросов, если монолог был длинным.</p><h4>Проводите шеринг и дебрифинг</h4><p>Шеринг — это когда мы дали блок информации или провели какую-то активность, а потом спрашиваем, какие у вас чувства, эмоции относительно этого, что думаете?</p><p>А дебрифинг — это рефлексия и обсуждение прожитого опыта. Например, когда на тренинге мы спрашиваем, что вы с собой забираете из этой практики? Приземляем на свой опыт, чтобы потом использовать в работе.</p><p><a href="https://tproger.ru/#0">Вернуться в начало</a></p><h3>Что делать с 1-2-1</h3><p>1-2-1 можно проводить регулярно, когда к команде присоединяется новый участник. Нужно отслеживать в каком он состоянии, как адаптируется.</p><p>Либо когда команда создается с нуля, они друг к другу притираются. Когда команда уже сбитая между собой, то 1-2-1 начинают тратить время. А если и менеджер новый, они вообще теряют смысл, потому что команде хорошо, и нужно вливаться уже менеджеру.</p><h4>Фиксируйте, что обсуждали ранее — лучше обезличенными формулировками</h4><p>У меня всегда были записки о предыдущей встрече, что я должен сделать на следующей. Я выбирал однозначные обезличенные формулировки, чтобы человек понял и не ушел в защиту. Например, если у меня есть вопросы к перформансу сотрудника, я спрашивал: «Если 100% перформанса, это тебя никто не отвлекает, то какой у тебя сейчас процент от 100%?». Мы выявляли, что не так, и тогда можно было давать обратную связь о том, что мешает человеку достичь результата. То есть я пытался заставить его рефлексировать, дать ответ и возможность помочь ему.</p><h2>Как давать обратную связь</h2><p>В обратной связи есть три компонента: источник сообщения, само сообщение и получатель. Есть еще помехи, которые осложняют восприятие сообщения получателем. Чтобы все было нормально, должно быть как можно меньше звеньев между источником и получателем обратной связи.</p><h3>Добавляйте конкретики</h3><p>К примеру, даже в похвалу. Недостаточно просто сказать — «Ты классный сотрудник». Человек может испытать какую-то эмоцию, но что делать с этой информацией, непонятно. Важно сказать, что именно вы сюда вкладываете, что именно мне нужно продолжать делать в моей хорошей работе, чтобы она продолжала быть хорошей.</p><h3>Сначала учите, а потом лечите и мочите</h3><p>Это метод, при котором вы объясняете человеку, почему его действия плохие. Возможно, он просто не знает. Когда вы убедились, что он вас понял, то объясняете, как делать правильно, а уже потом даете фидбэк. До стадии «мочить» я не доходил, обычно во второй мы пытались выправить поведение.</p><p>Также есть метод «Пинок»:<br />P — это позитив,<br />IN — что-то, что можно доработать,<br />N — это негатив,<br />OK — самая важная часть — понял ли человек обратную связь.</p><p>Многие менеджеры команды сопровождения рассказывали, что команда просит не использовать метод сэндвича — позитивная обратная связь, потом критика, потом снова что-то приятное.</p><p>Когда какие-то изменения подаются с этими смягчениями, ты начинаешь задумываться, а что имелось в виду? Сейчас у людей идет запрос на честность — просто скажите нам открыто, что происходит. Но, конечно, в рамках здоровой коммуникации. Мы <a href="https://tprg.ru/NKOD">в Практикуме</a> за бережный подход и уважение.</p><p>Если студент направил работу с большим количеством ошибок, ревьюеру все равно нужно написать, что получилось хорошо, даже если кажется, что позитивных моментов нет. Человек учится, и курсы — это песочница для проб и ошибок. Наша задача — поддержать студента и помочь побыстрее оттуда выйти с помощью объяснений и подсказок. Проверяя очередную работу студента и повторяя один и тот же комментарий много раз, важно помнить, что их будет смотреть живой человек со своими эмоциями и ощущениями. Поэтому каждую работу важно проверять одинаково качественно.</p><p><a href="https://tproger.ru/#0">Вернуться в начало</a></p><h2>Как разрешать конфликты</h2><p>Открытые конфликты — это прекрасно. Можно сразу разобраться, что произошло, и поговорить, либо с привлечением тимлида, либо самим.</p><p>Но когда все находятся в happy bubble, делают вид, что все хорошо, и при этом продолжают работать скрепя зубами, уже сложнее.</p><h3>Выводите людей на чистоту</h3><p>Можно сначала попробовать на 1-2-1 собрать фактуру, то есть вообще разобраться, что происходит. Это создаст безопасную, доверительную атмосферу. А дальше уже обязательно честно со всеми поговорить. Это тоже про культуру — мы не замалчиваем проблемы, а умеем открыто их принимать.</p><p>Может помочь также метод тухлых рыб. Это фасилитационная сессия, где ты помогаешь доставать конфликты, их коллективно обсуждать. Суть в том, что предлагаются определенные предметы, метафоры, чтобы разбавить атмосферу. И идея в том, что каждый вываливает, что ему нравится, не нравится, без привязки к личностям, но к действиям, конкретным событиям и фактам. И важно именно снять напряжение и научиться говорить открыто. А результатом может быть что угодно. В конце может оказаться, что команду надо расформировывать, так как они не могут работать друг с другом, это тоже нормально.</p><h2>Как проводить брейнштормы</h2><p><b>Самые ходовые инструменты:</b> Miro, Jam, Draw.io, Microsoft Whiteboard, Plant UML</p><p>Главное правило брейншторма — делиться абсолютно всеми идеями, которые приходят в голову, даже если они кажутся вам странными. Поэтому при генерации также нужно соблюдать и другое правило — не критиковать, а дополнять и докручивать мысли коллег. В брейншторме обычно участвуют либо отдел, либо команда, то есть много человек, поэтому нужно продумать, как организовывать большое количество людей, которые еще и будут высказываться.</p><p>Важно ответить на такие вопросы:</p><ul><li>Кто модератор?</li><li>Где проводить брейншторм?</li><li>Как потом транслировать результаты?</li><li>Сколько времени нужно заложить на выступление?</li><li>Как эффективно выстроить коммуникацию?</li></ul><h3>На брейншторме должна быть «строгая» модерация</h3><p>Модерирует обычно организатор. Он отвечает на вопросы в чате, отслеживает поднятые руки и реакции в камерах, следит за таймингом и корректным выполнением заданий. Например, чтобы на первом этапе участники не критиковали никого, а скидывали все, что придумывается. А помимо этого организатору еще бы здорово подготовить пространство, в котором все будут работать, попросить кого-то все записывать.</p><p>Важно считать не чистое время, а грязное, то есть учитывать технические и коммуникационные особенности. Например, есть механика, когда в группе из пяти человек высказывается каждый. Модератор планирует по минуте на высказывание, но забывает про переходы между людьми, а ведь они тоже занимают время. В итоге из запланированных 5 минут, группа выйдет за тайминг и получится 6-7. И так в каждой активности формируется снежный ком выхода за тайминг.</p><h3>Разбивайте большую команду на маленькие группы</h3><p>Если людей на встрече много, несколько десятков, то они с неохотой включаются в беседу. Тогда лучше разбивать их на маленькие группы, а потом объединять результаты из каждой комнаты, либо вовлекать через механики, которые не требуют подавать голос публично, в Miro стикеры поперекидывать, например.</p><p>Мы использовали Miro и аналоги: в Meet есть Jam, Draw.io, но они не такие удобные. Для брейнштормов архитектуры мы готовим UML-диаграммы, их можно редактировать в режиме реального времени. То есть кто-то пишет код, и он появляется на картинке. Потом его можно перекинуть в Notion.</p><p>В работе не всем удобно, что Whiteboard сильно отдельно от Jira и Confluence. То есть когда база знаний в одном месте, а идеи генерируются в другом месте, приходится постоянно это между собой актуализировать.</p><p><a href="https://tproger.ru/#0">Вернуться в начало</a></p><h2>Как организовать трекер</h2><p><b>Самые ходовые инструменты:</b> Jira, Notion, Redmine, Яндекс.Трекер, ClickUp</p><p>Трекер — это всего лишь инструмент. Кто-то может им не пользоваться, им достаточно написать на доске цель и идти к ней. А кому-то нужно отслеживать количество часов работы, чтобы вести учет для статистики, отчетов и т. д.</p><h3>Выделите у цели четкие стадии работы</h3><p>У нас есть трекер задач, мы выстраиваем там ближайший спринт с расписанной целью. У нее есть четыре состояния: заведена, в процессе, достигнута, не достигнута. Как именно эта цель достигается, мне, как менеджеру, уже не так важно. Какие-то команды в трекере задач сидят полностью, какие-то меньше и пользуются внутренним блокнотом, а потом раз в неделю прикладывают задачи в трекер, как лог. Кто-то на внутреннем Wiki ведет дейлики.</p><h3>Кастомизируйте любой инструментарий под свой бизнес-процесс</h3><p>Первый трекер, которым я пользовался, был Redmine. Но лучше всего оказался Jira. У меня такой синдром утенка, и я знаю, как все устроено в Jira, поэтому я стараюсь везде сделать как в Jira. Сейчас мы используем Яндекс.Трекер, он похож на Jira. В любом случае инструментарий нужно дорабатывать под свой бизнес-процесс.</p><p>Например, я использовал ClickUp, но там нельзя давать тикет по номеру. В Jira это очень удобно: название проекта, несколько букв и номер. Он легко запоминается, в ClickUp такой штуки нет. Там ID какой-то просто набор букв, и ты никогда в жизни его не запомнишь.</p><h3>Используйте Kanban-доски</h3><p>У нас командная работа организована в Notion по Kanban. Есть 4 вертикальные колонки: сделаны, в работе, в плане на спринт и жду ответ. А в горизонтальных колонках указаны ответственные. И, соответственно, они скрываются, открываются как выпадающие списки, то есть мы можем всегда зайти и посмотреть, кто что из нас делает.</p><h3>Добавляйте фишки в оформление</h3><p>Наша милая фишка — в название задачи мы добавляем эмодзи. Это помогает расслабиться, потому что ты не только видишь текст, но и понимаешь, что вы находитесь в приятной интересной атмосфере.</p><p>Мне нравится ценность <a href="https://tprg.ru/NKOD">Практикума</a> про честность и открытость. Важно, чтобы люди не боялись сказать, что у них не получилось. Если не успели выполнить задачку в спринт, так и говорим, что я не успела, переношу на следующий. Это нормально, я и чувствую себя комфортно и безопасно, всегда знаю, что если вдруг что, то меня поймут, поддержат.</p><p>Notion начинает хорошо продвигаться в продуктовой разработке. Но какие-то технические моменты там не совсем удобно заливать. У разработчиков не очень хорошо получается работать в Notion, фронтендер еще с ним дружит, потому что понимает, как он работает. А вот бэкендеров тяжело туда переманить.</p><p><a href="https://tproger.ru/#0">Вернуться в начало</a></p><h2>Как организовать общение в течение дня</h2><p><b>Самые ходовые инструменты:</b> Telegram, Mattermost, Slack, Discord</p><h3>Мемы — это тимбилдинг</h3><p>Команда эффективно работает, когда она работает слаженно. Для этого необходимы тимбилдинги, неформальное общение. Если нет возможности устроить командный выезд, то мемы — неплохая замена. Можно создать чат, где коллеги будут обмениваться приколами. Это помогает разгрузить и сплотить команду.</p><h3>Заведите отдельный канал для разгрузки</h3><p>У нас был канал в Discord, где можно было поделиться честным мнением о чем-то. Если ты не хочешь быть токсичным, но при этом хочешь выразить то, что тебе совсем не нравится, ты можешь написать туда: бесит Flask — и слить в канал свои переживания.</p><h3>Сообщайте коллегам свои рабочие часы</h3><p>Мы советуем нашим наставникам и ревьюерам создавать отдельную папку в мессенджере под работу, чтобы разделять контекст личной жизни и работы. У нас все работают в разных часовых поясах, мы относимся к этому с пониманием и заранее обговариваем удобное время для встреч. Те, кто живут в другом часовом поясе, выстраивают свой рабочий день совсем по-другому: если мы созваниваемся вечером, у них только утро. Удобно, если в мессенджере есть возможность поставить статус или в описании зафиксировать, по какому часовому поясу ты живешь. В наши времена это супер актуально.</p><h2>Как в развитии софтов может помочь школа Яндекс Практикума</h2><p>У каждого из нас свой опыт обучения и, честно говоря, не всегда хороший. Мы всё время наблюдали, как люди транслируют свой опыт: родители, учителя в школе или преподаватели в университете. Кто-то делал это мягко и доходчиво, а кто-то директивно и сложно. Важно понимать, что этих людей редко кто учил, как можно мягко и эффективно передавать свой опыт другим.</p><p>Мы же стараемся подготовить наших экспертов как раз к такой передаче опыта и обучаем следующему:</p><ul><li>как учатся студенты;</li><li>созданию безопасной атмосферы, в которой комфортно проходить обучение;</li><li>здоровой коммуникации со студентами: мягкому tone of voice, письменной коммуникации, алгоритмам решения сложных ситуаций;</li><li>работе с эмоциональным интеллектом;</li><li>как давать обратную связь;</li><li>инструментам передачи опыта;</li><li>проведению онлайн-вебинаров.</li></ul><p>Наставник передает свой опыт с помощью двух основных сред: общаясь со студентами в чате и проводя вебинары. У каждой среды есть свои особенности. Например, в рамках проведения вебинаров есть ряд инструментов публичного выступления в онлайне, которые мы можем применять в рамках обучения.</p><p>При этом у нас нет цели превращать вебинар в шоу, а основная задача наставника — добиться образовательного результата для студентов. Поэтому человеку нужно научиться проводить вебинары и передавать опыт в них. Вот чему мы учим для эффективного проведения вебинара:</p><ul><li>составлению структуры вебинара;</li><li>групповой динамике и взаимодействию с участниками;</li><li>технической подготовке (важно помнить про стабильный интернет, камеру, ровный фон и отсутствие посторонних шумов);</li><li>публичным выступлениям в онлайне: расположению в камере, работе с голосом, объяснению сложных идей.</li></ul><p>Важно проговаривать любые детали, которые кажутся банальными, так как не для всех очевидно, что это влияет на внимание аудитории. Все это применимо и к рабочим созвонам, разница между обучением и работой невелика — и там, и там вы взаимодействуете с людьми и что-то им транслируете.</p><p>Благодаря подготовке к работе со студентами и постоянной практике передачи опыта, наши эксперты быстро развивают свои софты, а значит, растут профессионально: из джунов в мидлы, из мидлов в синьоры. Помимо того, что эксперт знает технические аспекты, он может их объяснить, эффективно делегировать задачи, дать развивающую обратную связь в корректной форме.</p><p>Школа наставников доступна всем, кто пройдет этапы отбора на роль наставника в Практикуме. Узнать, какие роли открыты сейчас, можно <a href="https://tprg.ru/NKOD">здесь</a>.</p><p><i>Реклама АНО ДПО «Образовательные технологии Яндекса», LjN8JtbN9</i></p>]]></content:encoded>
    </item>
    <item>
      <title>5 ошибок Python-разработчиков, которые выдают новичка</title>
      <link>https://tproger.ru/articles/5-owibok-python-razrabotchikov--kotorye-vydayut-novichka</link>
      <comments>https://tproger.ru/articles/5-owibok-python-razrabotchikov--kotorye-vydayut-novichka?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вика Овсянникова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/5-owibok-python-razrabotchikov--kotorye-vydayut-novichka</guid>
      <description><![CDATA[<p>Рассказали, какие основные ошибки делает начинающий Python-разработчик, как их можно избежать и на что в целом обращать внимание.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/5-owibok-python-razrabotchikov--kotorye-vydayut-novichka">5 ошибок Python-разработчиков, которые выдают новичка</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Баги и ошибки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 20 Feb 2024 08:20:02 GMT</pubDate>
      <content:encoded><![CDATA[<h2>1. Неряшливость в коде</h2><p>« from main.tasks import task »</p><p>Это не только код по PEP, сколько отсутствие видимой логики и структуры в коде.</p><p>Главное, что надо всегда помнить, что код должен быть в первую очередь читаемым, а в идеале — еще и понятным. Тут нет предела совершенству, но в целом есть несколько простейших рекомендаций, которые позволят избежать даже не ошибок, сколько нелепых небрежностей.</p><p>По тексту я использую синтаксис Django ORM, так как он визуально проще, чем тот же SQL, и его можно представить как псевдокод для SQL.</p><h3>Давайте понятные имена функции и переменным</h3><p>Вместо c = count()</p><p>Вместо q = Model.objects.all()</p><p>Если будут большие связки из фильтров и прочего, можно</p><p>Вместо for x, y in _dict.items()</p><p>Вместо count = User.objects.count()</p><p>Кто-то решит, что лучше count_user, но я думаю, это индифферентно, главное, чтобы было понятно.</p><p>Бывает обратная ситуация, когда название становится слишком длинным, чтобы вынести всю логику. Например, произвольная функция</p><p>может быть существенно упрощена несколькими способами.</p><p>Нормально:</p><p>Здесь у нас «хардкод», который лучше избегать всегда, кроме сonst-значений. Тем не менее, в отличие от довольно спорной оригинальной функции, эти имеют шанс на переиспользование.</p><p>Хорошо:</p><p>Теперь функция не привязана к конкретным типам покемонов, мы можем переиспользовать ее много раз. Жаль только, что в случае, если нам нужно посчитать в целом несколько типов за раз.</p><p>Отлично:</p><p>Строгий пример полиморфизма, мы больше не привязаны ни к конкретным типам покемонов, ни к тому, скольких надо посчитать за раз. Нужны пикачу? Отправляем одного пикачу, нужны пикачу и чармандеры, отправляем их списком и получаем нужный результат.</p><p>Поэтому я бы использовал окончательно вот такой вариант:</p><p>В предыдущем варианте происходит как минимум на одну проверку больше, если вводные данные не подходят по типу. К тому же там функция, у которой происходит возврат данных. Хочется, чтобы он был в конце, потому что туда обычно и смотрит программист, что пришло, что вернул, а потом уже в тело.</p><p>Если не получается вложить весь смысл в наименование функции или переменной, не пишите «x = » или «def func», а попробуйте разнести логику.</p><h2>2. Отсутствие базовых знаний Linux/Unix</h2><p>Наверняка в англоязычных зарубежных компаниях встречается разработка на Windows, но все же Python, как скриптовый язык, работает на Unix и Linux.</p><p>Оптимальная разработка проходит на Mac или Ubuntu (и других любимых дистрибутивах), с целью выложить код на сервер, который, скорее всего, будет на Ubuntu. Более того, в вакансиях начиная с мидла, а иногда с джуна, спрашивают базовые знания Linux. В идеале вы должны уметь использовать еще несколько утилит, которые помогут в работе или деплое, уметь создавать пользователей, группы, менять группы и права директориям.</p><h3>Утилиты которые точно пригодятся в использовании</h3><p><b>Supervisor</b></p><p>Позволяет запускать и отслеживать процессы. Например, чтобы при перезапуске системы у вас всегда запускался проект.</p><p><b>Cron</b></p><p>Утилита, которая позволяет выполнять команды стартуя их в конкретный момент времени, например, парсить сайт каждый понедельник, в два часа ночи.</p><p><b>Nginx</b></p><p>Широко используемый веб-сервер. Если у вас веб-проект, без него практически не обойтись.</p><h2>3. Слабые знания СУБД</h2><p>Джуна могут взять без опыта или знаний работы с СУБД, но в целом полезно знать, для чего обычно используется та или иная база данных, какие лучше использовать и в каких случаях.</p><h3>Нереляционные СУБД</h3><p>С нереляционными попроще, мы вот используем Redis, MongoDB и Clickhouse.</p><p>Redis — это key-value хранилище. Если провести аналогию с питоновскими типами — словарь, который отлично подходит для хранения кэша. Удобно использовать для межкомпонентного взаимодействия.</p><p>MongoDB — это хранилище JSON-документов, по такой же аналогии — список json’ов. Отлично подходит, если у вас где-то генерируется большое количество данных, которые вы не хотели бы терять. Например, если бы все эти данные писали в реальном времени в реляционную: это долго, могут случится ошибки записи, плюс нужна валидация и прочее. Mongo же пишет эти данные как есть, а в соседнем сервисе вы можете спокойно загрузить все данные в реляционную базу в своем темпе, пройдя все нужные валидации и проверки.</p><p>Clickhouse — можно сказать, новинка отечественных разработчиков, использует SQL язык, но при этом не является реляционной БД. Основная задача — хранение гигантского количества данных и возможность с ними работать. Собрать миллиард записей и посчитать сумму тех или иных записей будет быстрее, чем в обычной реляционной БД. Хорошо сжимает данные, можно разворачивать кластером, привычный SQL язык.</p><p>Подойдет, чтобы смотреть статистику, основанную на больших объемах данных.</p><h3>Реляционные СУБД</h3><p>Это, в первую очередь, MySQL и Postgres. Скорее всего, разработка на Python при участии БД будет проходить с помощью ORM, Django ORM или SQLAlchemy. Если это SQLAlchemy, это не так страшно, так как написание запросов в этой ORM так или иначе следует логике SQL. Django ORM максимально своеобразный.</p><p>Вот один и тот же способ получить имя пользователя и название его работы</p><ul><li>в Django ORM:</li></ul><ul><li>в SQLAlchemy:</li></ul><ul><li>чистый SQL:</li></ul><p>В «Алхимии» видно, что мы что-то все-таки присоединили, Django ORM же работает с другими абстракциями. Надо понимать, как работают запросы и чистый SQL. Например, в случае профилирования, если тормозит страница, и экран профилирования покажет некий запрос, который вызывается либо несколько раз, либо отъедает время загрузки. Тогда нужно этот запрос отыскать в коде. Опять же, если SQL из примера будет съедать 10 секунд загрузки, выше видно, как он будет выглядеть в «Алхимии» или в «Джанго ОРМ».</p><h2>4. Забивание на чистоту GIT</h2><p>Частая проблема, которая не просто запускается, но еще и поддерживается коллективом. У вас мастер-ветка выглядит как</p><p>«fix -&gt; fix -&gt; fix -&gt; try -&gt; featyre -&gt; fix -&gt; revert» ?</p><p>Самое популярное оправдание, которое я слышал на эту тему, мол, что привязался, работает же. Фактически да, так оно и есть, но код это не только про «работает же». Это еще и его сопровождение в будущем. Представьте, вас просят что-нибудь найти или пофиксить спустя кучу времени, а вы натыкаетесь на странный кусок кода. В истории GIT одно «feature-&gt; feature -&gt; feature»? И коллега, судя по аннотации GIT, даже работает, но тоже не может вспомнить, в каком бреду это было написано и по чьей просьбе.</p><h3>Поэтому, как по мне, близкое к идеалу ведение коммитов:</h3><p><b>Одна задача — один коммит.</b> Несколько разработчиков на одной задаче? От каждого разработчика по коммиту. В тексте коммита ссылка на трекер задач на конкретную задачу. В будущем, если возникнут проблемы, можно легко посмотреть, в рамках чего писался тот или иной код.</p><p>Помимо самих коммитов, есть еще использование rebase и merge.</p><p>Самое оптимальное использование <b>merge — только для мастера</b>. Во всех остальных случаях отлично показывает ребейз, история будет чище и меньше конфликтов.</p><p>Начинаете работу над задачей? <b>Делаете новую ветку от мастера</b> (или как скажет тимлид).</p><p>Заканчиваете работу над задачей? <b>Делайте финальный ребейз на мастер и посквошьте коммиты</b>. Работаете с коллегой в одной ветке? Вместо мержа origin-ветки в локальную, делайте ребейз на ориджин-ветку. История коммитов будет чище, сложно будет в ней теряться.</p><h2>5. Написание тестов</h2><p>Я не буду про TDD. Это приходит с годами. Выдаст в вас новичка не только отсутствие тестов, так и написание тестов на каждый чих. Нужно сразу понять, где начинается бизнес-логика, и покрывать ее тестами.</p><p>Это зависит от компании, но я встречал следующий паттерн: бизнес-логику покрывают тестами, какие-то рядовые действия — нет. Бизнес-логикой компания зарабатывает там, где ошибки принесут финансовые результаты. Да, может, если не получится сохранить VK в профиле, клиент обидится, и вы его потеряете, но лучше потратить время на обкладывание тестами того функционала, которым зарабатывает ваш клиент.</p><p>Понимание, что является бизнес-логикой, а что нет, это придет с опытом. Могу лишь порекомендовать чаще расспрашивать менеджеров, чем именно занимается компания и как выглядят процессы снаружи кода, с точки зрения обычного персонала. Универсальный совет, поможет вам лучше понимать свой продукт.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что нужно знать перед тем, как работать с контейнерами</title>
      <link>https://tproger.ru/articles/chto-nuzhno-znat-pered-tem-kak-rabotat-s-kontejnerami</link>
      <comments>https://tproger.ru/articles/chto-nuzhno-znat-pered-tem-kak-rabotat-s-kontejnerami?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вика Овсянникова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/chto-nuzhno-znat-pered-tem-kak-rabotat-s-kontejnerami</guid>
      <description><![CDATA[<p>Рассказываем, почему все пользуются технологией контейнеризации и зачем знать про namespaces и системные вызовы при работе с контейнерами.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/chto-nuzhno-znat-pered-tem-kak-rabotat-s-kontejnerami">Что нужно знать перед тем, как работать с контейнерами</a>»</p>]]></description>
      <category><![CDATA[Операционные системы]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты командной строки]]></category>
      <category><![CDATA[DevOps]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 20 Nov 2023 11:50:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Все умеют работать с контейнерами, но мало кто понимает, как работают они сами. Это ограничивает возможности диагностики и управления программным обеспечением и средствами контейнеризации. DevOps-практики оказываются неэффективными, а безопасность просаживается. Разбираемся, на каких уровнях могут возникнуть ошибки, где можно зайти в тупик и что нужно знать, чтобы этого не произошло.</p><h2>Почему мы пользуемся контейнерами</h2><p>Разработчики пользуются библиотеками. Они позволяют не изобретать велосипед и пользоваться готовыми функциями.</p><p>К сожалению, если появляется новый дистрибутив, подтягиваются и новые библиотеки. Все они поставляются с определенными версиями ОС, и их нужно тестировать в различных средах. А также закрывать уязвимости, делать бэкпорт фич с учетом старых версий библиотек на более ранних дистрибутивах. И учитывать, что в разных дистрибутивах могут различаться стандарты размещения файлов — FHS.</p><p>Чаще всего ресурсов на поддержку такого «зоопарка» нет. Чтобы избежать постоянных тестов совместимости, слой библиотек передается вместе с docker image, из которого разворачивается тот самый контейнер. В нем есть всё необходимое в переносимом виде.</p><h2>Чем технология контейнеризации отличается от виртуализации</h2><p>У операционной системы есть абстракция-посредник между программами и оборудованием — ядро, которое обеспечивает API-вызовы. Происходит это через системные вызовы. Чаще всего они относительно низкоуровневые, надежные и примитивные. Например, «считай 10 байт, открой файл, считай 10 байт». Большинству программ этого достаточно.</p><p>Обратите внимание на эти системные вызовы: с помощью fopen можно смотреть, по какому пути открывается файл, а с clone(fork) и exec — видеть, как идут запуски тредов и внешних программ в контейнере.</p><p>Если, например, приложению приходится работать с аппаратными токенами, то нужны полноценные драйверы. Здесь может понадобиться виртуализация, где гипервизор host-OS позволяет запустить любое ядро с нужными драйверами.</p><p>Получается матрешка. Есть железо, на нем работает программа — ядро. Она создала процесс и запустила в нем гипервизор. Тот создал абстракцию железа, в которой запустилась программа-ядро. Оно создало процесс, в котором запустило программу. Не про это ли думал Нолан, когда снимал «Начало»?</p><p>При контейнеризации мы остаемся на уровне абстракции ядра (на котором запущены контейнеры), и так же отправляем туда системные вызовы. Процесс не знает о файловой системе и компьютере, он получает всю необходимую информацию из ядра.</p><p>Что почитать: «Linux. Системное администрирование», Р. Лав. В книге описаны системные вызовы, происходящие в Linux. Помогает понять, как работает ОС «с пользовательской точки зрения».</p><h2>Что такое namespace</h2><p>Ядро умеет изолировать программы друг от друга через процессы. Между ними изоляция осуществляется благодаря namespaces, которые позволяют отвечать на системные вызовы каждого приложения по-разному. Это как с билетами на концерт: по одному можно пройти на танцпол, по другому — в ложу, по VIP — пообщаться с артистом за кулисами.</p><p>С помощью Linux namespace мы показываем процессу, что ядро должно отдавать ресурсы из особой области, где распакованы docker image или лежат socket-файлы сетевых подключений.</p><p>Нужно понимать, за что отвечают такие namespaces:</p><ul><li>Файловая система (Mount) — область файловой системы, которая предоставляется приложению как корневая.</li><li>UTS — собственный hostname.</li><li>PID — позволяет в различных пространствах давать одинаковые ID процессам.</li><li>Сети (Network) — позволяет изолировать ресурсы, связанные с сетью. При этом процессы из разных пространств видят свои сетевые устройства, таблицы маршрутизации, правила firewall и так далее.</li><li>Межпроцессное взаимодействие (IPC) — позволяет организовать межпроцессное взаимодействие.</li><li>Пользовательские ID (User) — позволяет иметь копию пользовательских и групповых идентификаторов.</li><li>Время (Time) — позволяет разным процессам видеть разное системное время или часовой пояс.</li></ul><p>Процесс в среде разработки ведет себя немного по-другому, нежели чем в контейнере после сборки. Обработка тех же системных вызовов и набор ресурсов, которые в итоге система вернет процессу при запросе, могут отличаться. Например, процесс запрашивает список пользователей, а он может быть вынесен в другой namespace. Или система может выдать другое дерево файловой системы, потому что базовый образ файловой системы для этого docker image от другого дистрибутива.</p><p>Также на удаленке многие разработчики начали работать в других временных зонах. Часто при дебаге тестировщики не могут найти ошибки в соседних системах из-за разницы временных меток в логах разработчика и системы.</p><p>Что почитать: «Внутреннее устройство Linux», Д. Кетов. В книге рассматриваются основные подсистемы ядра и их сущности: файлы и файловые системы, виртуальная память и отображаемые файлы, процессы, нити и средства межпроцессного взаимодействия, каналы, сокеты и разделяемая память.</p><p>Посмотрим на конкретных примерах, какие проблемы можно отследить.</p><h3>Кейс №1: выросло System time</h3><p>Процесс может потреблять большое количество процессорного времени. Можно диагностировать это стандартными средствами. Посмотреть на цифры в выводе команды TOP:</p><ul><li>US( user time);</li><li>SY (system time);</li><li>WA (wait);</li><li>IO wait и так далее.</li></ul><p>Если разработчик не понимает, за что эти цифры отвечают, он решит, что накосячил где-то в коде. Хотя на самом деле процесс может потреблять много system time.</p><p>Пример из практики: информационная безопасность экспериментировала с журналами аудита и добавила дополнительные правила. По графикам системы мониторингов Grafana нагрузка на сервере подскочила процентов на 30. В этот же день был релиз фронтенд-приложения. Естественно, все решили, что фронт начал атаковать бэкенд, и именно он потребляет больше ресурсов даже не на пике.</p><p>Первая идея — искать, какие REST-вызовы дали нагрузку. На сервере мы видим, что на самом деле выросло SY. В процессе разработчики поняли, что были добавлены новые правила аудита, и решили их убрать. Тогда нагрузка вернулась к обычным стандартным значениям.</p><p>Что почитать: «Сетевые операционные системы», В. Олифер, Н. Олифер. В книге рассматриваются концепции и принципы построения большинства современных ОС.</p><h3>Кейс №2: ошибка в документации</h3><p>Команда писала приложение, в котором использовались криптографические подписи. После сборки и упаковки в Image, все перестало работать, это типичная ситуация. Ошибка говорит, что недоступен файл, который точно должен быть доступен. Базовый образ минималистичен, утилит диагностики в себе не содержит. Для разработчика это тупиком.</p><p>Проблема решилась обычным strace на компьютере разработчика, в котором довольно быстро удалось выудить ошибку системного вызова fopen. Оказалось, в документации криптобиблиотеки ошибка, и монтировать файлы с закрытым ключом нужно по другому пути. Разработчик же пользовался графическим интерфейсом криптобиблиотеки, и о путях знал только из документации.</p><h2>Что в итоге</h2><p>Технология контейнеризации — это не магическая виртуализация. По большому счету, понятие процесса, в котором запускаются программы, никуда не делось. Просто с точки зрения операционной системы он вынесен в отдельный пул ресурсов. Если разработчик понимает, какие стандартные средства операционной системы задействованы в процессе, он сможет диагностировать и ошибки, которые могут возникать в том числе при запуске приложений в контейнерах.</p>]]></content:encoded>
    </item>
    <item>
      <title>Зачем нужны комментарии в коде и как их правильно писать</title>
      <link>https://tproger.ru/articles/zachem-nuzhny-kommentarii-v-kode-i-kak-ih-pravilno-pisat</link>
      <comments>https://tproger.ru/articles/zachem-nuzhny-kommentarii-v-kode-i-kak-ih-pravilno-pisat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[МТС]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/zachem-nuzhny-kommentarii-v-kode-i-kak-ih-pravilno-pisat</guid>
      <description><![CDATA[<p>В статье собрали лучшие практики написания комментариев в коде и рассказали, когда лучше обойтись без них.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/zachem-nuzhny-kommentarii-v-kode-i-kak-ih-pravilno-pisat">Зачем нужны комментарии в коде и как их правильно писать</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 07 Sep 2023 11:07:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>При создании программы разработчики должны не только написать рабочий код, но и сделать его понятным для команды. В этом помогают комментарии.</p><p>Комментарии — аннотации, которые делают код более доступным и удобным для чтения и понимания.</p><p>В этой статье подробно рассмотрим, зачем нужны комментарии и как их правильно писать. А также обсудим, как избегать излишнего объяснения.</p><h2>Когда комментарии — хорошая идея?</h2><p>Разработчики тратят гораздо больше времени на чтение и понимание кода, чем на его написание. Поэтому хорошие комментарии значительно упрощают работу команды.</p><p>Вот главные плюсы использования этого инструмента:</p><ul><li><b>Сохранение знаний. </b>Разработчики могут приходить и уходить из проекта, но хорошие комментарии остаются — помогают передать знания о коде новым членам команды.</li></ul><ul><li><b>Улучшение взаимоотношений. </b>Хорошие и полезные комментарии помогают создать более дружелюбную и продуктивную атмосферу в команде.</li></ul><ul><li><b>Эффективная отладка. </b>Понятные комментарии сокращают время на поиск и устранение ошибок. Разработчики могут быстро понять, какие задачи стояли, и быстро локализовать проблему.</li></ul><ul><li><b>Улучшение понимания. </b>Когда разработчик возвращается к своему коду спустя некоторое время, хорошие комментарии позволяют ему быстро вспомнить, почему код написан именно так, а не иначе.</li></ul><h2>Типы комментариев для разных задач</h2><p>Выделяют несколько типов комментариев, у каждого из них — своя роль в объяснении и документировании кода. Сгруппируем их по смыслу и рассмотрим на примерах.</p><h3>Поясняющие комментарии</h3><p>Полезны для объяснения выбора конкретных алгоритмов или принятых компромиссов. А также пояснений, почему, наоборот, не использовали более очевидный код, если у этого были негативные последствия.</p><p>Пример кода на Python с поясняющими комментариями, объясняющими алгоритм сортировки пузырьком:</p><p>В этом примере поясняющие комментарии объясняют каждый шаг алгоритма сортировки пузырьком: как внешний цикл проходит по всем элементам, как внутренний цикл выполняет перестановки и как наибольший элемент «всплывает» на свою позицию в конце списка. Это помогает разработчикам понять логику алгоритма и его реализацию в коде.</p><h3>Документирующие комментарии</h3><p>Нужны для создания формальной документации, которая может быть автоматически сгенерирована в виде отдельных документов или встроена в код.</p><p>Такие комментарии описывают общие принципы работы, структуру и основные функции программы. Важно понимать разницу между документирующими и поясняющими комментариями, чтобы использовать их эффективно и улучшить качество кода.</p><p>Вот некоторые примеры документирующих комментариев:</p><ul><li><b>Описание неочевидных классов и методов.</b> Документирующие комментарии предоставляют информацию о назначении, параметрах и возвращаемых значениях методов и функций.</li><li><b>Генерация документации.</b> Документирующие комментарии предназначены для автоматической генерации документации к коду и часто используются с инструментами типа Doxygen (для C++, C и других языков), Javadoc (для Java) и docstrings (для Python).</li></ul><p>Допустим, у нас есть класс, реализующий алгоритм решения квадратного уравнения на Python:</p><p>Здесь документирующие комментарии описывают назначение класса QuadraticSolver, параметры его конструктора, метод solve() для решения уравнения и формат возвращаемых значений. Эта документация помогает понять, как использовать класс для решения квадратных уравнений, при этом она не избыточна и охватывает ключевые аспекты функциональности кода.</p><h3>Отладочные и временные комментарии</h3><p>Иногда в коде есть места, которые очевидно требуют доработки или улучшения в будущем. В таких случаях удобно использовать распространённые отладочные комментарии.</p><h4>#TODO: Планирование рефакторинга</h4><p>Этот комментарий используют, чтобы указать на необходимость будущего <a href="https://tproger.ru/articles/kod-kak-u-senora-refaktoring/">рефакторинга</a>. Он обозначает, что конкретная часть кода требует доработки или оптимизации.</p><h4>#FIXME: Исправление проблем</h4><p>Комментарий #FIXME используют, когда обнаружили проблему или баг в коде, который нужно исправить. Этот тег помогает указать на участок кода, который может вызвать проблемы.</p><h4>#Warning: Осторожный запуск</h4><p>Тег #Warning подразумевает, что перед запуском соответствующего участка кода нужно быть осторожным из-за возможных проблем.</p><h4>#Error: Обозначение ошибки</h4><p>Комментарий #Error используют, чтобы указать на ошибки в коде. Здесь вы можете объяснить проблему и причину, почему её не удалось решить.</p><p>Пример кода с отладочными комментариями на Java:</p><p>В примере комментарий FIXME указывает на необходимость обработки случая деления на ноль, а комментарий TODO предполагает добавление логирования перед выполнением деления для последующей отладки.</p><h2>Правила хорошего тона при комментировании</h2><p>Написание хороших комментариев — искусство, требующее внимания к деталям и понимания потребностей. Пишите комментарии только там, где они действительно необходимы.</p><p>Вот несколько практик, про которые нужно помнить:</p><ul><li><b>Комментарии должны отвечать на вопрос «почему» </b>и объяснять причины, алгоритмы и принятые решения, а не просто повторять код. Код сам по себе описывает, «что» он делает, а комментарии помогают понять, «почему» и «как».</li></ul><ul><li><b>Не забывайте обновлять комментарии вместе с кодом.</b> Код часто меняется, а комментарии остаются нетронутыми, что может привести к недоразумениям. Не забывайте пересматривать и обновлять комментарии вместе с изменениями кода.</li></ul><ul><li><b>Соблюдайте стиль. </b>Следуйте единообразному стилю комментирования в вашем проекте. Это поможет сохранить код читаемым и профессиональным.</li></ul><ul><li><b>Предпочитайте самодокументирующийся код. </b>Идеальный код должен быть настолько понятным, что большая часть комментариев становится излишней. Если в коде используют сложные алгоритмы, комментарии могут стать незаменимым инструментом для объяснения логики и шагов выполнения. Но даже здесь стоит стремиться к тому, чтобы код был как можно более понятным.</li></ul><ul><li><b>Сначала — хороший код. </b>Комментарии могут иногда прятать в себе недостатки кода, позволяя разработчику оставить сложный или непонятный код в надежде, что комментарий всё объяснит. Это неправильный подход. Лучше всего сначала упростить код до уровня, на котором он станет понятным без дополнительных комментариев.</li></ul><ul><li><b>Включайте контекст.</b> Если код связан с какой-либо задачей, багом или требованием, включите ссылку на соответствующий номер или описание, чтобы разработчики могли понять, почему определённое решение было принято.</li></ul><ul><li><b>Код-ревью и коллективная работа. </b>Практика код-ревью — отличный способ улучшить качество комментариев. Отзывы и рекомендации от коллег могут помочь выявить недостаточно понятные комментарии или предложить альтернативные способы их написания.</li></ul><h2>Когда комментарии в коде — плохая затея?</h2><p>В некоторых случаях комментарии могут быть лишними или даже вредными. Понимание, когда не следует писать комментарии, также важно, чтобы не перегружать код лишней информацией и обеспечить читаемость.</p><p>Вот несколько случаев, когда комментарии могут быть не нужны:</p><ul><li><b>Очевидные детали и стандартные конструкции.</b> Не стоит комментировать простые и хорошо известные аспекты кода, которые понятны из его структуры. Комментарии, объясняющие базовые элементы языка программирования (например, что такое “for” или “if”), будут лишними.</li><li><b>Неудачные попытки пояснения.</b> Иногда попытка написать комментарий может только запутать, если код плохо структурирован. Вместо этого попробуйте улучшить сам код, чтобы необходимость в объяснении отпала.</li></ul><ul><li><b>Излишне подробные описания. </b>Если комментарии становятся слишком подробными и детальными, они могут усложнить чтение кода. Оставьте подробные объяснения для документации, а в коде пусть будут только ключевые моменты.</li></ul><ul><li><b>Негативные комментарии.</b> Не стоит выражать негодование или критику чужого кода — комментарии не предназначены для этого. Обсудить ход работы и качество кода лучше напрямую с разработчиком.</li></ul><h2>Подведём итог</h2><p>Хорошо написанные комментарии могут значительно улучшить понимание и поддерживаемость вашего кода, а плохие — привести к путанице, ненужным сложностям и разозлить коллег.</p><p>Написание хороших комментариев — баланс между полезным объяснением кода и избеганием избыточности.</p><p>Кто умеет писать хорошие комментарии, поделитесь опытом, как к этому пришли.</p>]]></content:encoded>
    </item>
    <item>
      <title>Моя бабушка сечёт в интерфейсе консоли, или Что важно знать про гипотезы для usability-теста</title>
      <link>https://tproger.ru/articles/moya-babuwka-sechyot-v-interfejse-konsoli-ili-chto-vazhno-znat-pro-gipotezy-dlya-usability-testa</link>
      <comments>https://tproger.ru/articles/moya-babuwka-sechyot-v-interfejse-konsoli-ili-chto-vazhno-znat-pro-gipotezy-dlya-usability-testa?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вика Овсянникова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/moya-babuwka-sechyot-v-interfejse-konsoli-ili-chto-vazhno-znat-pro-gipotezy-dlya-usability-testa</guid>
      <description><![CDATA[<p>Как придумывать, формулировать и откуда брать гипотезы для UX-исследований, и какие гипотезы считать эффективными.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/moya-babuwka-sechyot-v-interfejse-konsoli-ili-chto-vazhno-znat-pro-gipotezy-dlya-usability-testa">Моя бабушка сечёт в интерфейсе консоли, или Что важно знать про гипотезы для usability-теста</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 16 Jun 2023 14:26:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Гипотезы для UX-тестов — это один из самых быстрых, простых и доступных способов тестирования интерфейсов. Хорошая проработка гипотез во многом залог успешного исследования, так как именно исходя из гипотез:</p><ul><li>выбирается метод исследования;</li><li>планируется сценарий исследования;</li><li>определяются границы исследования.</li></ul><p>Я хочу поделиться опытом подготовки гипотез для исследования в нашей <a href="https://kas.pr/tproger-design-nefedyeva-uxui">команде UI/UX, где рождается дизайн всех продуктов Kaspersky</a>. Расскажу, как именно мы используем этот метод, но главное — кто должен придумывать гипотезы, откуда их брать и как придумать хорошую.</p><p>Вместо дисклеймера замечу, что все критерии «хороших» и «плохих» гипотез актуальны и для других типов исследований, однако в usability-тестах есть своя специфика, поэтому мы будем разбирать на их примере.</p><p>Для начала договоримся, что такое гипотезы и зачем они нужны, потому что научное определение одно, а его практические толкования для разных профессий могут плавать. Итак, согласно книге «<a href="https://www.isras.ru/publ.html?id=1373">Социологическое исследование: методология, программа, методы</a>» «гипотезы — обоснованные предположения о структуре объектов, характере связей между изучаемыми явлениями и возможных подходах к решению социальных проблем».</p><p>А теперь простыми словами ?</p><p>Гипотезы — предположения о тестируемом объекте (интерфейсе и т. д.), о том, почему и как с ним взаимодействует пользователь, которые необходимо проверить для достижения цели исследования.</p><p>С терминологией и областью применения разобрались, но как с ними работать на практике?</p><h2>Кто должен придумывать гипотезы</h2><p>Здесь нет правильного ответа. Расскажу о том, как это делаем мы.</p><p>В Kaspersky исследования проводятся внутри рабочих групп, состоящих из: заказчика исследования (обычно это продакт-менеджер), дизайнера, маркетинг-менеджера, технического писателя и, конечно, исследователя. Мы не ограничиваем круг лиц, которые участвуют в подготовке и обсуждениях, поэтому приложить руку к исследованию могут все, кто причастен к разработке тестируемого продукта. Каждый из участников рабочей группы готовит свой список гипотез (и даже хорошо, если у нескольких участников рабочей группы они будут одинаковые).</p><figure><img src="https://media.tproger.ru/uploads/2023/06/bffb3cc6-69d3-454d-a7a1-6cd844304bf5-autoconverted.jpeg" alt="" /></figure><h3>Почему мы так делаем</h3><p>У каждого участника рабочей группы разные уровни и фокусы экспертизы, разная степень вовлечённости в тестируемый продукт. Поэтому каждый из коллег смотрит на продукт со своей, особой точки зрения, концентрируясь на своей области.</p><p>Признаться, у исследователя в этом круге обычно меньше всего экспертизы, так как он работает с несколькими продуктами и погружен в них не так глубоко, как остальные участники команды. Он своего рода медиатор в группе, направляющий команду в правильное русло. В результате мы получаем максимально исчерпывающий список гипотез.</p><p>При таком способе подготовки гипотез мы:</p><ol><li>Частенько дорабатываем макеты, потому что участники рабочей группы уже до тестов видят часть проблем.</li><li>Сильно облегчаем себе обработку результатов (дальше я расскажу почему и как) и можем процентов на 80 предугадать результаты исследования, и лишь 20 будут неожиданными инсайтами.</li></ol><h2>Откуда брать гипотезы</h2><p>Именно из этого вопроса от коллег и родилась эта статья. Одно дело, когда исследователь одиноко собирает UX гипотезы в своём рабочем файлике, другое дело, когда задачу приходится делать всей команде, которая раньше могла этим никогда не заниматься.</p><p>Поделюсь чек-листом источников, которые стоит учесть:</p><ul><li>статистику, собираемую в продукте;</li><li>личный опыт использования продукта;</li><li>обращения в службу поддержки;</li><li>не всегда, но иногда также можно исследовать отзывы, обзоры конкурентов.</li></ul><p>Коллегам я предлагаю метод «бабушки из Урюпинска». Это собирательный образ, не в обиду Урюпинску и бабушкам, тем более что моя, например, активно ставит лайки своим подружкам в социальных сетях. В чём он заключается?</p><p>Каждому участнику рабочей группы я предлагаю абстрагироваться от накопленного в своей сфере опыта, знаний о современных технологиях и представить себя той самой «бабушкой из Урюпинска». Пройти путь пользователя как эта бабушка, каждую секунду задумываясь о том, где она может сделать или понять что-то не так.</p><figure><img src="https://media.tproger.ru/uploads/2023/06/acb62857-7b04-44e2-853c-7055c3d5433b.png" alt="" /></figure><p>Все эти мысли в потоке записывать в заметки, Word, куда угодно, подписав экраны, на которых эта мысль появилась. Важно быть внимательным и открытым: смотреть не только на очевидные вещи, которые запланированы сценарием, но и представлять, что ты случайно нажал куда-то не туда, свернул страницу, закрыл и т. д.</p><p>Признаться, этот способ отлично работает для B2C продуктов, и не до конца учитывает специфику B2B продуктов. Но всё же применим. Ведь почему, например, какому-нибудь системному администратору должно быть хуже, если даже бабушка просечёт интерфейс его рабочей консоли? ?</p><h2>Как придумать «хорошую» гипотезу</h2><p>Постараться соблюсти следующие правила:</p><h3>Гипотеза должна соответствовать цели исследования</h3><p>Наверное, это самое важное при подготовке гипотез. Перед исследованиями можно частенько столкнуться с ситуациями «а давайте проверим ещё вот это», «давайте узнаем ещё вот это», что не всегда соответствует цели, границам исследования и выбранному методу. Поэтому необходимо проверять, не пытаемся ли мы объять необъятное? Вероятно, для тестирования этих гипотез можно провести отдельное исследование, если это необходимо.</p><p>Не нужно создавать гипотезы ради гипотез. Для каждой решите, что будете делать, если она подтвердится. Или не подтвердится. Если с результатами проверки гипотезы ничего нельзя сделать, то от неё стоит отказаться.</p><h3>Гипотеза должна быть проверяема</h3><p>Мы адаптируем сценарий исследования под гипотезы и наоборот, однако случаются ситуации, когда гипотеза становится непроверяемой. Например:</p><ul><li>Когда мы тестируем один функционал, а гипотеза относится к другому. Например, мы проводим тестирование процесса оформления заказа в корзине, а гипотеза относится к категориям меню. Увы, эту гипотезу проверять мы не будем.</li><li>Когда гипотеза не соответствует выбранному методу исследования.</li></ul><p>Разные методы исследований позволяют получать ответы на разные исследовательские вопросы, это напрямую касается и гипотез.</p><p>Например, гипотеза «<i>Новые, более крупные фото в карточке товара увеличат конверсию на 15%</i>» — отличная, но она подходит для A/B-тестирования, а не для usability-теста, так как в них мы не можем оценивать степень чего-либо при сравнении.</p><p>Или UX гипотеза «<i>Пользователей раздражает, если им приходит больше трёх уведомлений в день</i>» — тоже отличная, но для дневникового исследования, потому что оценивает поведение тестируемого объекта вне рамок теста.</p><p>Так же, как и в прошлом случае, если гипотеза кажется важной, но не соответствует выбранному методу, то это повод задуматься о смене метода исследования.</p><figure><img src="https://media.tproger.ru/uploads/2023/06/ad0363f8-b8c1-46ef-9c30-2b524db910bc.png" alt="" /></figure><h3>Хорошая гипотеза должна быть конкретной</h3><p>Постарайтесь строить как можно более точные и однозначные гипотезы. Она должна быть сформулирована так, чтобы избежать разночтений. Как это сделать?</p><ol><li>Чем более дробный шаг пути пользователя или мельче элемент интерфейса, который вы анализируете, тем лучше.</li><li>Здорово, если гипотеза содержит в себе предпосылки наблюдаемого поведения.</li></ol><p>Давайте разберём на примере покупки в Интернет-магазине.</p><p>❌«Непонятно, как оплатить заказ»</p><p>✅«Непонятно, как ввести данные новой, а не привязанной ранее карты»</p><p>Подтвердив первую UX-гипотезу, мы не знаем, что с ней делать. Да, непонятно, но в какой именно момент? Такие результаты чреваты разночтениями между участниками рабочей группы, в них легко увидеть желаемое, а не действительное. Сами понимаете, к чему это может привести.</p><p>Приведу несколько примеров неконкретных и конкретных гипотез:</p><p><b>Неконкретная:</b> Пользователь не выдаст разрешение на использование геолокации.</p><p>Почему? Не поймёт, для чего оно? Не заметит этот элемент интерфейса? Не поймёт, что это именно разрешение на отслеживание геолокации? Принципиально откажется от выдачи разрешения, зная, что это сильно уменьшит возможности приложения?</p><p><b>Конкретная: </b>Пользователь не поймёт, что выдача разрешения на использование геолокации необходима для корректной работы основной функции приложения.</p><p><b>Неконкретная:</b> Пользователь не поймёт условия доставки.</p><p>Также неясен ряд вопросов. Не поймёт, что это доставка до ПВЗ, а не домой? Не поймёт, что привезут завтра, а не сегодня? Не поймёт, что для получения потребуется паспорт? И т. д.</p><p><b>Конкретная:</b> Пользователь не заметит, что доставка платная.</p><p>В этих примерах неконкретные гипотезы не так плохи по многим критериям, но из них будет сложно сделать однозначные выводы.</p><p>Именно для того, чтобы формулировать конкретные UX-гипотезы мы ранее самостоятельно знакомились с тестируемым объектом и беспристрастно изучали все узкие места. Это помогает понимать, где именно могут возникнуть проблемы.</p><h2>Как сформулировать гипотезу</h2><p>Некоторые формулируют гипотезы как открытые вопросы, и в целом так можно делать, но я предпочитаю этого НЕ делать и расскажу почему.</p><p>В моём мире идеальная гипотеза должна быть сформулирована так, чтобы на неё по результатам теста можно было ответить «да, это так», либо «нет, это не так». Так, хорошая гипотеза должна иметь возможность быть подтверждённой.</p><p>Из гипотезы-вопроса довольно легко сделать гипотезу-утверждение. Давайте попробуем вместе.</p><p><b>Гипотеза-вопрос:</b> Как пользователь увеличит количество товара для заказа?</p><p>Мы знаем, что это можно сделать несколькими способами. Именно их и формулируем в гипотезы-утверждения:</p><ul><li>Гипотеза-утверждение 1: Пользователь увеличит количество товара в заказе через +1 в корзине.</li><li>Гипотеза-утверждение 2: Пользователь увеличит количество товара через +1 в карточке товара.</li><li>Гипотеза-утверждение 3: Пользователь попробует добавить тот же товар в корзину ещё раз.</li></ul><p>У нас получилось 3 гипотезы-утверждения, на которые можно ответить «да, это так» и «нет, это не так». В результате мы:</p><ol><li>Заранее продумали все (или основные) пути, которыми может пойти пользователь для решения своей задачи, поэтому поведение пользователей будет для нас более предсказуемым.</li><li>Это сильно облегчит дальнейшую обработку результатов.</li></ol><h2>Почему хорошо подготовленный список гипотез облегчает анализ результатов</h2><p>Если вы хоть раз проводили usability-тест, скорее всего, вы знакомы с чек-листами. Для остальных расскажу про удобный и простой инструмент для работы с результатами тестов. Мы с коллегами используем для анализа Excel-таблицы, где в столбцах прописываем вопросы и гипотезы в строках номеров респондентов.</p><figure><img src="https://media.tproger.ru/uploads/2023/06/3b63c6a6-5297-4b57-9a12-83da3be563f6.png" alt="" /><figcaption>Выглядит это примерно так</figcaption></figure><p>Так, при заполнении этого файла достаточно ставить 0, если гипотеза не подтвердилась, и 1, если подтвердилась. Внизу после всех тестов можно посчитать сумму по всем подтверждённым гипотезам через формулу. В результате:</p><ol><li>Мы легко считаем встречаемость проблем, какие гипотезы были подтверждены, а какие опровергнуты.</li><li>Да, подготовка такого чек-листа занимает время, но зато заполнять его быстрее. Вместо того, чтобы писать словами «вышел из корзины, снова начал искать товар и добавлять его, чтобы увеличить количество» мы просто ставим 1, где гипотеза подтвердилась, и 0, где нет. Благодаря этому, мы можем не пересматривать в записи большинство тестов.</li></ol><p>Вот и всё, что важно знать про гипотезы для успешных тестирований. Если вам было интересно, то приходите к нам <a href="https://kas.pr/tproger-design-nefedyeva-uxui-vacancies">в Kaspersky в команду UI/UX</a> — будем прорабатывать и предугадывать пользовательский опыт вместе ? Ну а если вы не только знаете, как выстраивать процесс исследований, но и умеете успешно пользоваться его результатами, а также хотите влиять на продукты на всех уровнях — от стратегии до имплементации — то, кажется, мы ждём именно вас на позицию <a href="https://kas.pr/tproger-design-nefedyeva-uxui-productdesignb2b">Product Designer B2B</a>.</p><figure><img src="https://media.tproger.ru/uploads/2023/06/04cd1526-bf30-435e-864f-ea310ca8f9d8.png" alt="" /></figure>]]></content:encoded>
    </item>
    <item>
      <title>Альтернатива if/else и switch: литералы объектов в JavaScript</title>
      <link>https://tproger.ru/translations/alternativa-if-else-i-switch-literaly-obektov-v-javascript</link>
      <comments>https://tproger.ru/translations/alternativa-if-else-i-switch-literaly-obektov-v-javascript?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Борисенков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/alternativa-if-else-i-switch-literaly-obektov-v-javascript</guid>
      <description><![CDATA[<p>Литералы объектов заменяют громоздкие условия в JavaScript списком пар ключ-значение: код читается лучше, а вызовов toLowerCase() становится меньше.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/alternativa-if-else-i-switch-literaly-obektov-v-javascript">Альтернатива if/else и switch: литералы объектов в JavaScript</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Для продолжающих]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Обучение программированию]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 22 Apr 2021 14:57:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Сложные условия в JS всегда были источником лишнего кода. Однако использование литералов объектов в JavaScript может избавить вас от этой проблемы. Давайте разберёмся, как это работает.</p><p>Литерал объекта в JavaScript — это список пар ключ-значение, перечисленных через запятую и обёрнутый фигурными скобками.</p><p>Допустим у нас есть функция, которая принимает на вход рифмованную фразу на <a href="https://habr.com/ru/company/englishdom/blog/439476/#:~:text=Кокни%20—%20один%20из%20самых%20известных,Британии%20и%20в%20частности%20Лондона.">английском сленге кокни</a> и возвращает её значение. Если использовать конструкцию if/else, то код будет выглядеть следующим образом:</p><p>Выглядит так себе. Этот код не только плохо читается, но и использует повторяющийся вызов функции toLowerCase().</p><p>Чтобы уменьшить количество кода, мы можем использовать дополнительную переменную или конструкцию switch.</p><p>Такой код выглядит чище, но это не предел. К тому же, в случае использования более сложных условий, можно случайно пропустить break и спровоцировать баги.</p><h2>Альтернатива</h2><p>Мы можем достичь той же функциональности используя объект. Вот пример, который выглядит гораздо аккуратнее:</p><p>Мы используем объект, ключи которого выполняют роль условий, а значения — результатов. Затем, с помощью квадратных скобок, мы проверяем наличие нужной строки. Так как полученная строка может быть null или undefined, то мы используем оператор Nullish coalescing (??). Таким образом мы избавляемся от null-значения, но не исключаем случай, что результат может быть равен нулю или false.</p><p>Это немного надуманный пример, но он иллюстрирует то, как использование ?? помогает избежать багов.</p><p>Подробнее о способах обработки <a href="https://tproger.ru/translations/how-to-handle-undefined-in-javascript/">undefined в JavaScript</a>.</p><h2>Сложная логика</h2><p>Для организации более сложных условий вы можете использовать в качестве значений свойств функции.</p><p>В этом коде мы выбираем необходимую функцию по ключу, а затем вызываем её с двумя аргументами. Так как мы используем опциональную цепочку, то функция вызовется только, если она существует. В противном случае вернётся дефолтное значение.</p><h2>Вывод</h2><p>Каждая условная конструкция имеет свою область применения. Для литералов объектов в JavaScript это длинные списки условий и сложные условия, которые можно реализовать с помощью функций.</p>]]></content:encoded>
    </item>
    <item>
      <title>Чаптеры, метрики, мотивация: что мы изменили в отделе тестирования REG.RU и как он устроен сегодня</title>
      <link>https://tproger.ru/articles/chaptery-metriki-motivacija-chto-my-izmenili-v-otdele-testirovanija-reg-ru-i-kak-on-ustroen-segodnja</link>
      <comments>https://tproger.ru/articles/chaptery-metriki-motivacija-chto-my-izmenili-v-otdele-testirovanija-reg-ru-i-kak-on-ustroen-segodnja?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/chaptery-metriki-motivacija-chto-my-izmenili-v-otdele-testirovanija-reg-ru-i-kak-on-ustroen-segodnja</guid>
      <description><![CDATA[<p>Перестройка тестирования в REG.RU в 2020 году: как распределённые по командам специалисты получили общий вектор развития, метрики и мотивацию.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/chaptery-metriki-motivacija-chto-my-izmenili-v-otdele-testirovanija-reg-ru-i-kak-on-ustroen-segodnja">Чаптеры, метрики, мотивация: что мы изменили в отделе тестирования REG.RU и как он устроен сегодня</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 18 Feb 2021 15:49:29 GMT</pubDate>
      <content:encoded><![CDATA[<p>Сайт <a href="https://www.reg.ru/">REG.RU</a> — большой и сложный продукт с микросервисной архитектурой. Над ним трудятся много разных распределённых команд. Все они самостоятельны и создают свой продукт, или же являются сервисом для других. У каждой свои разработчики, менеджеры и, конечно, тестировщики.</p><p>Во многом из-за этого специалисты по тестированию в разных командах хоть и входили в общий отдел тестирования, но мало работали вместе и не были объединены. Ситуация изменилась в 2020 году. Сегодня я расскажу, что удалось сделать и что в итоге получилось.</p><h2>Почему перемены были необходимы и что изменилось?</h2><p>Последние несколько лет все тестировщики работали распределённо со своими командами. Отдела тестирования в его классическом понимании не было, что не позволяло применять новые подходы, внедрять новые инструменты. Из общего были только инструменты автоматизации, но процессы тестирования отличались. Также не было общего вектора развития, ведения тестовой документации (только предпринимались попытки).</p><p>Чтобы избежать подобного рассинхрона в отделах, и чтобы новые инструменты внедрялись оперативнее — в REG.RU появились чаптер-лиды. Мы в отделе тестирования дифференцировали технический чаптер и чаптер обучения. Перед каждым из них стоят свои задачи, у каждого свой workflow работы.</p><p>Помимо выделения чаптеров и совместной работы, в отделе появилась своя система мотивации, общие метрики и roadmap развития. Расскажу подробнее обо всех изменениях.</p><h2>Цели и работа чаптеров и чаптер-лидов</h2><h3>Что такое чаптер?</h3><p>Чаптер — это группа специалистов (обычно 7-9 человек) одного направления работы со сходными узкопрофессиональными навыками в одной сфере технических компетенций (hard skills). Например, чаптер бэкенд-разработчиков или UX-дизайнеров.</p><figure><img src="https://media.tproger.ru/uploads/2021/02/96046_09fd8ef3_camyaVgC_1613130170.png" alt="" /><figcaption>Разница между командами и чаптерами</figcaption></figure><p>Цель чаптера — развивать профессиональные знания, умения и навыки сотрудников. Чаптер не занимается конкретным продуктом или сервисом. Его фокус — развитие компетенций и технической экспертизы. Благодаря обмену опытом внутри чаптера участники узнают новые инструменты и методы работы.</p><h3>Кто такие чаптер-лиды?</h3><p>Чаптер-лид — это лидер чаптера, помогающий его участникам достигать целей чаптера. Он должен быть практикующим экспертом в своём направлении.</p><p>Чаптер-лиды помогают каждому члену команды найти точки роста и эффективно обучаться, а также ставить и выполнять цели отдела.</p><p>Зоны ответственности чаптер-лидов:</p><ul><li>постановка целей развития отдела в компании (регламенты, идеи, экспертная оценка);</li><li>обучение (организация процесса, фокусировка на целях обучения в чаптерах);</li><li>поиск точек роста и развития сотрудников, мониторинг динамики роста и развития сотрудников (индивидуальный план развития);</li><li>мониторинг и контроль психологического комфорта сотрудников (performance review, оценка 360, обратная связь, one-to-one встречи);</li><li>вопросы по профессиональному росту и зарплате;</li><li>взаимодействие с PO (Product Owner), SDM (Service Delivery Manager), SRM (Service Request Manager) при сложностях в команде.</li></ul><p>Сейчас в нашем отделе два чаптер-лида, которые поделили между собой зоны ответственности на ручное и автоматизированное тестирование. Прокачкой чаптер-лидов занимается руководитель отдела, он же ставит им задачи и цели.</p><h3>Чаптер обучения</h3><p>По мере становления отдела стало понятно, что все тестировщики очень разные по своим навыкам и техническим скиллам. А так как формировались единые процессы тестирования и автоматизации, то необходимо было прокачать некоторых сотрудников до нужного уровня. Для этого мы и создали чаптер обучения, его основная цель — развитие необходимых скиллов и повышение знаний в области тестирования на практике.</p><h3>Работа в чаптере обучения</h3><p>Для начала мы совместно с чаптер-лидами сформировали бэклог необходимых умений, которые понадобятся в дальнейшей работе над задачами и, используя скрам с трёхнедельными спринтами, стали брать задачи по теории и практике. Тестировщики в течение трёх недель выполняют задания, обмениваются опытом, делают доклады.</p><p>Примеры задач в чаптере:</p><ul><li>рассмотреть редакторы баз данных;</li><li>изучить продвинутый Xpath оси и операторы;</li><li>изучить методы CodeceptJs;</li><li>изучить, как генерируется и из чего состоит Allure отчёт.</li></ul><h3>Технический чаптер</h3><p>Чаптер сформировался из прокаченных тестировщиков, чтобы изучать и продвигать новые инструменты в отделе, а также выполнять внешние запросы от других команд из области тестирования.</p><h3>Работа в техническом чаптере</h3><p>Изначально мы организовали работу по скраму, тоже сделали свой бэклог работ. Но так как задачи сами по себе сложные и трудоёмкие, то трёх недель было недостаточно для их выполнения. Поэтому мы перестроились и стали использовать канбан.</p><p>Еженедельно на митинге мы планируем, что будем брать в работу, в зависимости от типа задач: внешний заказчик, задачи по roadmap отдела тестирования и другие. Поток задач стал равномерным и прогнозируемым.</p><p>Примеры задач в чаптере:</p><ul><li>автоматизация тестирования REG.API;</li><li>подтянуть свежие образы браузеров;</li><li>поправить хрупкость тестов deploy;</li><li>оптимизировать архитектуру тестов.</li></ul><h3>Совместная работа отдела</h3><p>Каждый отдельный чаптер, как уже отмечалось, работает по своему workflow: чаптер обучения — скрам, технический чаптер — канбан (изначально был скрам).</p><p>На еженедельных митингах тестировщики обсуждают задачи, которые сейчас в работе, проводят стримы (совместная демонстрация новых инструментов и других вещей).</p><p>Раз в три недели проводят планирование, груминг и ретроспективу.</p><p>Общая ретроспектива со всеми тестировщиками проводится раз в квартал для фокусировки на roadmap отдела.</p><h2>Как изменилась мотивация сотрудников?</h2><p>Когда в отделе наладилась работа и все тестировщики объединились, была придумана система мотивации для сотрудников. Чтобы интерес к задачам не угасал и появлялись новые идеи по улучшению текущих процессов.</p><h3>Отгул «‎за молодец»</h3><p>Раз в месяц чаптер-лиды определяют тех, кому положен отгул. Сотрудник может получить его, когда берёт на себя дополнительные активности по чаптерам, отделу или командам, например:</p><ul><li>поделиться знаниями вне общих задач;</li><li>проверить задания в чаптерах;</li><li>за заслуги перед отделом (сделать проще и лучше жизнь отдела, усовершенствовать какие-либо процессы);</li><li>переработки (вынужденные и подтверждённые менеджером или руководителем);</li><li>замещение сотрудника:выполнение тикетов во время отсутствия;проверка отчётов во время отсутствия;помощь более четырёх часов на другом проекте.</li></ul><h3>Обучение в компании</h3><p>В дополнение к новым методам мотивации остались те, что давно работают в REG.RU. Например, программа внешнего обучения, где каждый может пройти курсы и съездить на конференцию за счёт компании. Также есть обширная онлайн-библиотека, корпоративный университет с тренингами, разработанными под потребности сотрудников. Доступна программа компенсации оборудования для удалёнщиков: можно прокачать не только скиллы, но и рабочее место.</p><p>Ещё в REG.RU есть программа офлайн-встреч (временно приостановлена на период пандемии коронавируса), чтобы распределённые команды могли синхронизироваться по рабочим вопросам и познакомиться поближе.</p><h2>Какие общие метрики появились в отделе?</h2><p>Качество работы отдела тестировщики подкрепляют метриками, которые собирают ежемесячно. Все метрики отображаются в одной общей таблице и отправляются командам для статистики. Также тестировщики обращают внимание команд на проблемы или увеличение количества пропускаемых багов.</p><h3>Количество потраченного времени на задачи или другие активности</h3><p>Назначение метрики</p><p>Для бизнеса необходимо измерять, сколько потрачено времени и на что, то есть сколько тратится из бюджета компании на тестирование, автоматизацию, работу чаптеров и прочие активности в командах.</p><p>Руководителю и чаптер-лиду важно видеть картину целиком и понимать, что если человек вырос и больше времени тратит на рутину, надо пересмотреть его деятельность.</p><p>Как считается</p><p>В нашей системе баг-трекинга на задачи автоматически можно ставить счётчики выполнения задач (таймер).</p><h3>Доля отфильтрованных дефектов</h3><p>Назначение метрики</p><p>Доля отфильтрованных дефектов — один из показателей качества, эффективность обнаружения багов (несоответствий требованиям). То есть какая доля дефектов была отфильтрована, а какая прошла на прод.</p><p>Как считается</p><p>Формула: количество багов, обнаруженных после выкатки, разделённое на общее количество багов, обнаруженных до и после выкатки.</p><p>Допустимый процент ошибок, которые были пропущены на прод, конечно же, будет зависеть от многих факторов. Однако если коэффициент получился &gt;0,1 — это плохо. Показатель означает, что каждый десятый дефект не был обнаружен во время тестирования и привёл к проблемам в ПО, уже переданном пользователям.</p><h3>Количество багов, пойманных автотестами на предпроде</h3><p>Назначение метрики</p><p>Метрика необходима, чтобы понять пользу e2e-тестов.</p><p>Как считается</p><p>Считается количество багов из отчёта Allure, которые автотесты не пропускают на прод.</p><h3>Тестовое покрытие требований</h3><p>Назначение метрики</p><p>Помогает следить за тенденцией изменений тестового покрытия, выявлять его слабые места, а также анализировать качество тест-дизайна.</p><p>Как считается</p><p>В Google-таблице на основании нашей карты функционала и тестов в TestLink. Считается вручную в %.</p><h3>Покрытие автотестами требований</h3><p>Назначение метрики</p><p>Отразить покрытие автотестами требований (тесты описываем в TestLink).</p><p>Как считается</p><p>Общее количество тест-кейсов, покрытых автотестами. Считается вручную в %.</p><h3>Скорость прохождения сквозных (e2e) тестов</h3><p>Назначение метрики</p><p>Проанализировать, за какое время проходят тесты и нужно ли оптимизировать скорость их прохождения.</p><p>Как считается</p><p>Считается из отчетов по автотестам (Allure) каждый месяц.</p><h3>Процент хрупкости e2e-тестов по командам</h3><p>Назначение метрики</p><p>Проанализировать, как часто у нас падают тесты и в каком количестве (в %), чтобы уменьшить количество падающих тестов путем их стабилизации или вообще отказаться от этих тестов.</p><p>Как считается</p><p>Собирается из отчетов по автотестам (Allure) каждый месяц.</p><p>После изменений работа в отделе тестирования стала прозрачной для всей компании: выстроились общие процессы, появился вектор развития, который отражается в roadmap. Удалось также внедрить множество новых идей. Среди них, например, общее ведение тестовой документации, оптимизация архитектуры кода, автоматизация REG.API, замер скорости динамических страниц.</p><p>Тестировщики прокачали soft skills и hard skills, а ещё — сплотились как команда. Для эффективной и слаженной работы важна комфортная обстановка и доверие к коллегам, поэтому появились неформальные встречи, игры, общение не только по работе. Благодаря всему этому мы стали продуктивнее и сплочённее.</p><figure><img src="https://media.tproger.ru/uploads/2021/02/96046_418c7d4a_hffviCYD_1613130170-autoconverted.jpeg" alt="" /><figcaption>Встреча команды тестирования REG.RU в Zoom</figcaption></figure>]]></content:encoded>
    </item>
    <item>
      <title>Код-ревью — как сделать правильно</title>
      <link>https://tproger.ru/articles/kod-revju-kak-sdelat-pravilno</link>
      <comments>https://tproger.ru/articles/kod-revju-kak-sdelat-pravilno?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kod-revju-kak-sdelat-pravilno</guid>
      <description><![CDATA[<p>Проверяющие смотрят, соответствует ли код стандартам команды и решена ли задача. Принципы, которые делают код-ревью полезным всем участникам.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kod-revju-kak-sdelat-pravilno">Код-ревью — как сделать правильно</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Dec 2020 07:41:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>Я оцениваю качество кода студентов в <a href="https://praktikum.yandex.ru/">Яндекс.Практикуме</a> на курсе «Мидл фронтенд-разработчик» и участвую во внутреннем код-ревью Яндекса. Мне кажется, что заложить принципы грамотного код-ревью — важная часть обучения. Благодаря этим навыкам студенты, с одной стороны, учатся писать хороший код быстрее, а с другой — делают в нём меньше ошибок.</p><h2>Принципы хорошего код-ревью</h2><p>Код-ревью — часть процесса разработки. В учебных проектах — это проверка написанного кода наставником, в рабочих проектах — коллегами. Они смотрят, насколько код подходит под стандарты, заданные в команде, выполняется ли поставленная задача и можно ли сделать реализацию лучше.</p><p>Чтобы сделать код-ревью полезным, всем участникам процесса нужно соблюдать несколько правил. Я расскажу о 7 принципах хорошего код-ревью.</p><h3>Уважайте друг друга</h3><p>Даже если вам кажется, что фраза «Я сегодня вышел на улицу, наступил в мокрый снег, и ощущения от этого были гораздо лучше, чем от вашего кода» очень смешная, и человек на неё не обидится, не стоит её использовать. Это можно сказать иначе: «Я думаю, в этом месте можно было бы сделать вот так». Будьте доброжелательны и помните, что вы оцениваете не человека, а его код. Это может казаться мелочью, но это база, из которой складывается здоровая атмосфера в команде и эффективная работа.</p><p>Полезные фразы «Я бы попытался сделать вот так…», «мне кажется, будет хорошей идеей попробовать вот это…», «в нашей команде мы как-то делали похожий проект и там мы решили эту задачу вот так…», «решение выглядит нелогичным, ты можешь попробовать применить вот такой подход, думаю, тебе это точно поможет».</p><h3>Будьте объективны</h3><p>Избегайте комментариев, которые состоят только из ваших субъективных оценок. Обязательно ссылайтесь на источники, документацию, материалы, которые помогут разработчику быстрее решить проблему.</p><p>Если вы столкнулись с тем, что человек написал код не так, как это принято на проекте, отправьте ему ссылку на код коллеги. И помните: аргументацию стоит писать уважительно — без подтекста «Как ты можешь этого не знать?»</p><p>Полезные фразы «Обычно такие вещи мы делаем немного иначе [ссылка на документацию], «вот ссылка на проект наших коллег — они очень здорово решили похожую задачу, думаю, мы можем перенять их опыт».</p><p>Пример из реального код-ревью:</p><figure><img src="https://media.tproger.ru/uploads/2020/12/image1.png" alt="" /></figure><h3>Не давайте готовое решение</h3><p>Этот принцип особенно важен в учебных проектах. Задача ревьюера-наставника — подтолкнуть человека в правильную сторону, подсказать, как ещё можно подступиться к задаче, какие инструменты можно использовать.</p><p>Полезные фразы «Вот хороший пример, как была решена почти такая же задача. Попробуй ознакомиться с альтернативным решением».</p><h3>Больше общайтесь с командой</h3><p>Некоторые моменты проще объяснить во время созвона или личной встречи. Не стоит считать такую коммуникацию тратой времени. На самом деле, отдача от такого созвона или встречи намного больше, чем кажется. Команда, которая умеет эффективно общаться — лучшая команда.</p><p>Полезные фразы «Если хочешь, можем созвониться, пообщаться голосом. Я буду рад подробно всё объяснить».</p><h3>Учитесь в процессе код-ревью</h3><p>В Pull Request для обсуждения изменений в коде могут прийти большие профессионалы с полярными мнениями. Если это случилось, не переживайте, наоборот, приготовьтесь узнать много нового. Однажды я делал задачу, связанную с браузерами, и в Pull Request пришли несколько опытных разработчиков. Они начали горячо обсуждать, как лучше хранить данные и какие решения для этого использовать. Внимательно прочитав и проанализировав их обсуждение, я узнал много нового и в итоге сделал задачу гораздо лучше, чем ожидал. Поэтому относитесь к код-ревью как к мощному инструменту для обучения.</p><h3>Несите ответственность за код</h3><p>Правильное отношение к код-ревью — считать, что вы несёте такую же ответственность за будущую корректную работу кода, как и его автор. Ведь от ваших комментариев зависит, насколько качественно человек выполнит свою работу.</p><h3>Внимательно относитесь к новичкам</h3><p>Если вам предстоит разобрать на код-ревью работу новичка, значит, нужно подойти к этому ещё ответственнее, чем обычно. От первой полученной обратной связи зависит, как человек будет работать в дальнейшем, а возможно, останется ли он вообще с вами. Чем ответственнее вы и ваши коллеги относятся к код-ревью, тем быстрее растут новички как профессионалы.</p><h2>Как код-ревью делают в Яндексе</h2><p>В дополнение к базовым принципам хорошего код-ревью, в Яндексе мы придерживаемся ещё трёх правил.</p><h3>Проверяем код на уровне логики и решений</h3><p>Во многих командах для автоматизированной проверки используются статические анализаторы кода — линтеры. Когда пунктуация и стиль кода проверяется автоматически, гораздо проще сфокусироваться на высокоуровневых вещах — насколько логично человек мыслит и насколько эффективные решения он принимает. Разработчики в Яндексе не жалеют времени и сил на полное погружение в код коллег.</p><h3>Не жалеем времени на код-ревью</h3><p>В Яндексе есть внутренний инструмент, который называется «ревьюшница». Это робот, который отправляет автоматизированные приглашения людям посмотреть код коллег, когда наступает время код-ревью. Принцип их отправления простой — пишем определённую команду в интерфейсе гитхаба, робот проверяет, кто имеет максимальную экспертизу в таком виде кода и присылает приглашения этим людям. Получается, чем больше кода писать, тем больше нужно ревьюить. В день это занимает около двух часов. Я никогда не жалею времени на это.</p><h3>Не заливаем код, который не прошёл ревью</h3><p>Во многих командах Яндекса эта вещь ограничена программно — новый код нельзя влить, пока он не получит определённое количество аппрувов (одобрений) от коллег. Это правило позволяет использовать в работе только качественный код.</p><h2>Чек-лист хорошего код-ревью</h2><p>Чек-лист поможет сделать ревью полезным, а коммуникацию — эффективной.</p><ul><li>Вы доброжелательны по отношению к коллегам в комментариях? Воздержитесь от шуток, которые могут обидеть.</li><li>Ваши сообщения содержат не только критику, но и конкретные предложения?</li><li>Каждый ваш комментарий подкреплён ссылками на документацию и примерами из кода?</li><li>Если вы делаете код-ревью в учебном проекте, не даёте готовое решение, а лишь подталкиваете человека в нужную сторону?</li><li>Не жалеете сил на обратную связь? Если чувствуете, что нужно дополнительно созвониться или написать в мессенджере, сделайте это.</li><li>Во время код-ревью думаете не только о соблюдении синтаксиса, но и пытаетесь понять, как именно мыслит человек при решении задач?</li><li>Понимаете, что написанный код нельзя считать готовым, пока его не посмотрит кто-то ещё?</li></ul><p>Если ваш ответ на большинство этих вопросов утвердительный, можно быть уверенным — код-ревью пройдёт успешно и станет для всех его участников хорошим инструментом для профессионального роста.</p>]]></content:encoded>
    </item>
    <item>
      <title>Стоит прочитать: обзор книги Карла Вигерса «Разработка требований к программному обеспечению»</title>
      <link>https://tproger.ru/books/obzor-knigi-karla-vigersa-razrabotka-trebovanij-k-programmnomu-obespecheniju</link>
      <comments>https://tproger.ru/books/obzor-knigi-karla-vigersa-razrabotka-trebovanij-k-programmnomu-obespecheniju?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/books/obzor-knigi-karla-vigersa-razrabotka-trebovanij-k-programmnomu-obespecheniju</guid>
      <description><![CDATA[<p>Издание описывает принципы, технологии и приёмы работы с требованиями, плюсы и минусы подходов и пригодится начинающим и опытным аналитикам.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/books/obzor-knigi-karla-vigersa-razrabotka-trebovanij-k-programmnomu-obespecheniju">Стоит прочитать: обзор книги Карла Вигерса «Разработка требований к программному обеспечению»</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Бизнес-аналитика]]></category>
      <category><![CDATA[Стоит прочитать]]></category>
      <category><![CDATA[Книги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 25 Nov 2020 15:17:34 GMT</pubDate>
      <content:encoded><![CDATA[<p>C «Разработкой требований к программному обеспечению» я познакомился на этапе перехода в отдел аналитики и проектирования систем, то есть, еще не будучи аналитиком. Очень вовремя! Книга помогла избежать ряда типовых ошибок и открыла мне глаза на многие базовые вещи, о которых я не знал. В ней подробно описаны основные принципы, технологии и приемы, четко показаны преимущества и недостатки различных подходов в разработке требований к ПО. Прежде всего, книга полезна тем, кто только начинает путь в аналитике, или хочет его начать. Но и для аналитиков с опытом, кто не читал книгу, будет много полезной информации. Считаю, что это настольная книга бизнес- и системного аналитика, как говорится, must read!</p><h3>«Основное следствие проблем с требованиями — переделка того, что, как вы думаете, уже готово»</h3><p>С самого начала книги автор доносит мысль о том, что переделки в проекте — это вещь неизбежная. Невозможно, чтобы заказчик и компания-разработчик сразу поняли друг друга в полной мере. Однако выявление необходимости переделок на разных этапах будет стоить совершенно по-разному. Наиболее безболезненно и наименее затратно исправлять ошибки, которые появились в ходе качественной работы квалифицированного аналитика с требованиями. В случае, если ошибка была обнаружена в ходе разработки, то помимо проекта придётся переделывать также и часть уже написанного кода. Если в ходе тестирования, то часть кода, которую потребуется скорректировать, будет еще больше. Теперь представьте, какие затраты будут, если ошибку обнаружит уже после завершения разработки и тестирования сам конечный пользователь и позвонит с жалобой? В книге автор приводит пример из собственного опыта, который был переведен в цифры одним из его клиентов. Трудозатраты на исправление дефекта внутри компании составляли 200 долларов. При этом исправление ошибки, когда она была обнаружена пользователем, уже составляла 4200 долларов, т.е. больше чем в 20 раз дороже. Книга доказывает, что качественная работа с требованиями — это не пустая трата ресурсов, а инвестиция в успех проекта.</p><h3>«Нигде более, как на стадии сбора требований, так тесно не связаны интересы всех заинтересованных в проекте лиц с успехом проекта»</h3><p>И здесь автор прав. После того как получен запрос или бизнес-задача от заказчика и до того момента, как стартовала разработка, то есть непосредственно написание кода, наступает то самое время, когда можно максимально приблизить проект к успеху. В этот момент начинается работа по выявлению, сбору, анализу, классификации, спецификации, документированию и управлению требованиями. Автор подробно описывает каждый из этих этапов, приводит живые (что самое интересное) кейсы, примеры ошибок и варианты исправления. Главное, чему учит книга — это как избежать тех самых ошибок при разработке требований к ПО. Требования представляют собой фундамент проекта, они являются основой для действий команд разработки и тестирования, для инженеров внедрения и для руководителей проектов. Поэтому от их проработки зависит успех всего проекта.</p><h3>«Разработка ПО включает по крайней мере столько же общения, сколько и обычная работа с компьютером, но зачастую мы делаем акцент на работе с компьютером и не уделяем достаточно внимания общению»</h3><p>Это одна из ключевых мыслей книги. Хорошие требования не появляются в процессе работы с документами на компьютере или в процессе самостоятельного моделирования прототипов и проектирования систем. Хорошие требования появляются в результате кропотливой и вдумчивой работы с людьми: со стейкхолдерами, пользователями системы, с разработчиками, тестировщиками и менеджерами проектов. Только в ходе общения с заинтересованными сторонами появляется возможность выявить их потребности и сформулировать требования к системе, по-настоящему удовлетворяющей их. В противном случае, есть риск получить понятный, красивый с яркими картинками документ для разработки системы, которая, в конечном счете не отвечает ничьим ожиданиям.</p><p>Такие ситуации иногда возникают у аналитиков в процессе работы над проектами, когда слишком много времени уделяется проектированию и документированию, и слишком мало – общению, сбору информации и получению обратной связи.</p><p>У меня был такой опыт. Я подготовил проект, отвечающий требованиям, обозначенным заказчиком в запросе. По моему проекту была выполнена разработка и произведено тестирование внутри проектной команды. Однако после отгрузки доработки системы и приёмки заказчиком выяснилось, что бизнес-цель, которая ставилась при создании запроса, не была достигнута. Разобравшись, я понял, что совершил классическую ошибку, о которой пишет Вигерс: увлекся процессами проектирования, уделив основное внимание решению технической задачи, обозначенной заказчиком в блоке требований. При этом я не учел требования бизнеса. Как выяснилось позже, в запросе присутствовали противоречивые требования. Их можно было бы устранить только в ходе общения с представителями бизнес-подразделений заказчика.</p><h3>«Лучше один раз увидеть, чем 1024 раза услышать»</h3><p>Еще один яркий момент, который произвел на меня впечатление, это то, как расписана важность визуализации требований. Точнее «комбинация текстовых и визуальных способов представления требований на различных уровнях абстракции».</p><p>У каждого типа представления свои преимущества, и они подходят каждый для своей цели. Например, при помощи изображений можно преодолеть барьеры, связанные с непониманием некоторых терминов внутри команды. Или при помощи диаграмм потока сообщений можно визуально быстро сориентироваться, в какой момент где какие данные передаются в системе. Автор приводит примеры ряда различных способов представления требований (карты, таблицы, диаграммы, прототипы) и поясняет, в каких случаях удобно использовать те или иные методы.</p><p>В книге много информации о самих процессах разработки требований, фактически – более половины книги. Все процессы описываются на живых примерах, в виде «стенограмм» с рабочих встреч с дальнейшим анализом с профессиональной точки зрения. На протяжении книги читатель оказывается постоянно вовлечен в участие в отдельных эпизодах большого проекта сложной системы. Это создает целостное представление о проблемах, с которыми сталкивается команда в ходе крупного проекта, и о методах, которые позволяют эти проблемы решать, и, самое главное, не допускать.</p><p>Детально расписаны роли участников проектов и их задачи, обоснована необходимость постоянного контроля обратной связи. Представлены методы управления требованиями, контроля внесения изменений, приемы моделирования и подходы к дизайну, а также рекомендации, как правильно фазировать процессы в жизненном цикле ИТ проекта.</p><p>В книге даже приведены шаблоны документов, которые, признаться, я по сей день частично использую в проектах. Подробно останавливаться на каждом описанном процессе не буду, их очень много. Понять, что можно узнать из книги легко – достаточно прочитать оглавление, оно очень подробное =).</p>]]></content:encoded>
    </item>
    <item>
      <title>CI/CD или конвейер качественного кода</title>
      <link>https://tproger.ru/articles/ci-cd-or-quality-code-pipeline</link>
      <comments>https://tproger.ru/articles/ci-cd-or-quality-code-pipeline?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/ci-cd-or-quality-code-pipeline</guid>
      <description><![CDATA[<p>CI/CD объединяет автоматические тесты, доставку и развёртывание кода, помогая реже пропускать баги на боевой сервис и сокращать потери времени.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/ci-cd-or-quality-code-pipeline">CI/CD или конвейер качественного кода</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Методологии разработки]]></category>
      <category><![CDATA[Product Development]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 20 Nov 2020 07:58:08 GMT</pubDate>
      <content:encoded><![CDATA[<p>Часто при релизе обновлений проектов возникают ситуации, когда по различным причинам на боевой сервис попадает баг, не выявленный прежде на тестовых серверах. И в связи с этим, у клиентов появляется ряд неудобств вплоть до полного отсутствия работоспособности проекта. Одно дело, если речь идет об одностраничном лендинге, и потенциальный покупатель не может найти контактов компании для связи, совсем другое — если мы имеем дело с интернет-магазином, который становится недоступным — это уже прямая потеря денег. Не говоря уже о финтех секторе, где каждая секунда неработающего сервиса выливается в колоссальные денежные потери.</p><p>Можно ли избежать страха и ненависти как клиента, так и конечных пользователей? Равно как и потери значительной части времени специалистов от общего количества часов разработки? Мы в <a href="https://chillicode.ru/">агентстве CHILLICODE</a> считаем, что да. И в этом нам может помочь применение методологии CI/CD.</p><p>Непрерывная интеграция (Continuous Integration, CI) и непрерывная поставка (Continuous Delivery, CD) представляют собой культуру, набор принципов и практик, которые позволяют разработчикам чаще и надежнее развертывать изменения программного обеспечения.</p><p>В свое время Генри Форд перевел создание автомобилей от ручной сборки к поточному конвейерному производству, перевернув тем самым промышленность своего времени. Принцип действия CI/CD (Continuous Integration &amp; Continuous Deployment) в чем-то похож на конвейер: методика выполняет интеграционную функцию, включая различные типы автоматических тестов на каждом этапе, с последующей доставкой и развёртыванием кода в готовый продукт для конечного пользователя.</p><p>Мы у себя с недавнего времени внедрили CI/CD в большинство  наших проектов, что позволило выстроить процесс релиза обновлений буквально в один клик, снизить риски появления багов, внедрив автотестирование и анализ качества кода перед каждым релизом, а также исключить особенности различий окружения хоста разработчика и сервера путем упаковки приложения в так называемые «контейнеры».</p><p>Многим клиентам, чей формат проекта подразумевает регулярные обновления, мы предлагаем работать с нами вместе с настроенной системой CI/CD. Потратив немного времени на старте на настройку окружения для непрерывной интеграции, мы сократим риски и сможем предложить клиенту высвободившиеся часы на более сложные задачи.</p><h2>Какие этапы релиза можно автоматизировать?</h2><h3>Тестирование и контроль качества</h3><p>Конечно же, об этом речь должна пойти в первую очередь. Тестировщиков нельзя полностью заменить, но львиную долю повторяющихся проверок можно автоматизировать путем модульного и регрессионного тестирования. Мы предлагаем автоматизацию в квадрате. CI/CD имитирует среду выполнения конечного сервера и запускает тесты автоматически, чтобы подтвердить работоспособность программного обеспечения и полностью исключить возможность выпустить в релиз продукт при возникновении ошибки. И клиент всегда будет уверен, что обновление не повредит работоспособности проекта.</p><h3>Сборка и упаковка кода</h3><p>Отныне разработчикам не нужно вручную подключаться к продакшн серверам проекта и с дрожащими руками пересобирать проект на боевом сервере. Пользователи более не увидят страницу «ведутся технические работы» во время обновления сайта благодаря методике «горячей подмены» контейнеров с развернутыми приложениями внутри.</p><h3>Открытая отчетность</h3><p>При добавлении клиента в репозиторий, он сможет самостоятельно отслеживать все процессы тестирования, сборки и доставки приложения на прод.</p><h2>Как обезопасить релиз?</h2><h3>В каких местах могут возникнуть проблемы?</h3><p>Стоит понимать, что автоматические модульные тесты и единая среда лишь фиксируют результат выполнения кода в определенных условиях, позволяя избежать нарушения их целостности в будущем. Однако в ситуациях, требующих ввода данных человеком, автоматизация может быть нежелательной или невозможной. Например, мы никогда не сможем автоматизировать приложение, когда дело доходит до удобства использования.</p><h3>Как предохраниться от логических ошибок функциональности и багов?</h3><p>Чтобы полностью исключить ошибки при деплое стоит комбинированно подойти к каждой итерации разработки продукта. Мы в CHILLICODE проводим релизы проектов в 5 этапов:</p><ol><li>После разработки новой фичи разработчик создает запрос на слияние ветки с новой функциональностью в основную ветвь приложения.</li><li>Тимлид проекта перед слиянием проверяет качество его кода и при возникновении проблем отправляет код на доработку.</li><li>После принятия запроса на слияние приложение проходит несколько этапов автоматического тестирования, анализа качества кода и разворачивается на нашем стейдж сервере.</li><li>Выделенный QA специалист дополнительно проверяет всю функциональность приложения на стейдж серверах перед показом клиенту.</li><li>И уже после одобрения клиентом всех правок мы отправляем приложение в релиз.</li></ol><h3>Что делать если критическая ошибка все равно осталась незамеченной и попала в прод?</h3><p>На этот случай мы держим в арсенале систему тегирования версий для каждого релиза и в случае возникновения критических ситуаций можем моментально откатить изменения до предыдущего состояния без необходимости вручную убирать участки ошибочного кода и заново проводить деплой приложения.</p><h2>Итого</h2><p>При работе с CI/CD мы имеем:</p><ul><li>Сокращение действий для деплоя до одного клика.</li><li>Снижение рисков появления потенциальных ошибок.</li><li>Автоматизация модульных и регрессионных тестов.</li><li>Щепетильный контроль качества кода.</li><li>Контейнеризация приложений и исключение различий среды разработки со средой выполнения.</li><li>Возможность моментального отката версии приложения при возникновении критических ситуаций.</li><li>Общее сокращение времени разработки на 10-20%.</li></ul><p>Внедряйте CI/CD в свои проекты, как это делаем мы в агентстве и пользуйтесь на здоровье. Если вы все еще сомневаетесь или остались какие-то вопросы, пишите комментарии и мы постараемся ответить.</p>]]></content:encoded>
    </item>
    <item>
      <title>Стили именования переменных и функций. Используйте их все</title>
      <link>https://tproger.ru/explain/naming-variables-and-functions-use-them-all</link>
      <comments>https://tproger.ru/explain/naming-variables-and-functions-use-them-all?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Борисенков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/explain/naming-variables-and-functions-use-them-all</guid>
      <description><![CDATA[<p>camelCase и другие соглашения помогают понять тип сущности с первого взгляда — идеального единого формата в программировании не существует.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/explain/naming-variables-and-functions-use-them-all">Стили именования переменных и функций. Используйте их все</a>»</p>]]></description>
      <category><![CDATA[Кодстайл]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Коротко о главном]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 18 Nov 2020 09:46:21 GMT</pubDate>
      <content:encoded><![CDATA[<p>Чтобы обеспечить легкую читаемость кода программисты используют разные стили именования для разных типов объектов, функций и переменных. Именно поэтому нет какого-то одного «идеального» формата. Выбор уместного стиля поможет быстро понять к какому типу относится сущность в коде, но не забывайте и о том, что имя должно объяснять что делает это сущность. Мы расскажем какой стиль существует для каждой из возможных ситуаций.</p><p>В программировании пробел является зарезервированным символом, поэтому все названия обходятся без него. Чтобы строки без пробелов всё же напоминали естественный язык и нужны все эти кейсы.</p><h2>camelCase (dromedaryCase)</h2><p>Каждое слово, кроме первого, начинается с большой буквы.<br />Применяется для именования переменных и функций в большинстве языков.</p><figure><img src="https://media.tproger.ru/uploads/2020/11/camelCase-1.png" alt="" /></figure><h2>PascalCase (CamelCase, StudlyCase)</h2><p>В этом стиле каждое слово начинается с заглавной буквы. Обычно используется для названий классов.</p><figure><img src="https://media.tproger.ru/uploads/2020/11/PascalCase-1.png" alt="" /></figure><h2>snake_case (pothole_case)</h2><p>Вместо пробела ставится нижнее подчёркивание. Используется в основном для имён полей баз данных, переменных и функций.</p><figure><img src="https://media.tproger.ru/uploads/2020/11/snake_case.png" alt="" /></figure><h2>SCREAMING_SNAKE_CASE (MACRO_CASE, CONSTANT_CASE)</h2><p>Тот же snake_case, только буквы всегда в верхнем регистре. Обычно используется для именования констант.</p><figure><img src="https://media.tproger.ru/uploads/2020/11/SCREAMING_SNAKE_CASE.png" alt="" /></figure><h2>kebab-case (dash-case, lisp-case)</h2><p>В этом случае пробел заменяется дефисом. Используется в URL и CSS. В языке Lisp так пишутся любые названия. Примеры:</p><p>background-color</p><h2>TRAIN-CASE (COBOL-CASE, SCREAMING-KEBAB-CASE)</h2><p>Все буквы в верхнем регистре, соединены дефисом. Применяется в языке COBOL для всех названий. Пример:</p><p>PROGRAM-ID</p><h2>Train-Case (HTTP-Header-Case)</h2><p>Каждое слово с большой буквы, соединены дефисом. Стиль названий HTTP заголовков. Пример:</p><p>Content-Length</p><h2>flatcase</h2><p>Все слова в нижнем регистре, без пробелов. Используется в тегах. Пример:</p><p>#stayhome</p>]]></content:encoded>
    </item>
    <item>
      <title>Программируем лучше с ESLint, Prettier и TypeScript</title>
      <link>https://tproger.ru/translations/setting-up-eslint-and-prettier</link>
      <comments>https://tproger.ru/translations/setting-up-eslint-and-prettier?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/setting-up-eslint-and-prettier</guid>
      <description><![CDATA[<p>Настройка ESLint и Prettier от простых правил до конфигов и плагинов помогает собрать своё окружение и меньше отвлекаться на форматирование.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/setting-up-eslint-and-prettier">Программируем лучше с ESLint, Prettier и TypeScript</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 01 Jul 2020 09:53:39 GMT</pubDate>
      <content:encoded><![CDATA[<p>В этой статье я хочу начать с самого простого и углубляться шаг за шагом. Для начала мы будем использовать простые правила и опции. Потом изучим использование конфигов и плагинов. На протяжении всего процесса вы будете получать полезные советы и информацию, чтобы на выходе создать собственное ESLint и Prettier окружение.</p><h2>Как все начиналось</h2><p>Вообще я не хотел использовать ESLint и Prettier, потому что Angular, который я использую в повседневной жизни, даёт мне нужные утилиты и простой инструмент форматирования кода. Но в итоге некоторые вещи заставили меня изменить свое решение об использовании ESLint и Prettier.</p><p>Во-первых, бесконечные споры о том, как писать и форматировать код. Это действительно надоевшая всем тема, по крайней мере, для меня. Личным предпочтениям тут не место. Есть более важные вещи, на которые стоит обратить внимание.</p><p>Во-вторых, я не хочу казаться беспомощным, когда коллеги начнут задавать вопросы во время демонстрации. Они потеряют интерес, если вы не отвечаете на вопросы или не сможете раскрутить тему.</p><p>В-третьих, мощь этих инструментов. Они помогают заниматься действительно важными вещами вместо того, чтобы отвлекаться от процесса, если, например, код отформатирован неправильно, на что чаще всего обращают внимание на code review.</p><p>И, в конце концов, это всё про бизнес, неважно насколько нам нравится то, что мы делаем. Это просто трата времени. Вы можете потратить его эффективнее.</p><p>Как вы заметили, у разработчиков в течение рабочего дня очень много отвлекающих факторов. Давайте устраним их, используя устоявшиеся инструменты.</p><p>Инфо Скорее всего поддержки TSLint в Angular не будет в ближайшее время т.к. TypeScript решили внедрить ESLint вместо TSLint. Команда Angular уже работает нам переездом с TSLint на ESLint. См. <a href="https://github.com/angular/angular-cli/issues/13732">issue</a>.</p><h2>Что такое ESLint, и как это может помочь нам?</h2><p><a href="https://en.wikipedia.org/wiki/Lint_(software)">ESLint</a> — это утилита, которая может анализировать написанный код. Фактически, это <a href="https://en.wikipedia.org/wiki/Static_program_analysis">статический анализатор кода</a>, и он может находить синтаксические ошибки, баги или неточности форматирования. В других языках, например, <a href="https://golang.org/">Go</a>, это является неотъемлемой частью языка программирования.</p><h2>Что я должен сделать, чтобы начать использовать ESLint?</h2><p>Я рассчитываю, что у вас уже установлены <a href="https://nodejs.org/en/download/">node</a> и <a href="https://www.npmjs.com/get-npm">npm</a>, и вы знакомы с ними.</p><h2>Создание рабочей папки</h2><p>Перейдите в папку, где находится ваш JavaScript или TypeScript проект, или, как я, создайте тестовую папку lint-examples, где мы можем работать по ходу статьи. В операционных системах на основе Linux наберите mkdir lint-examples в командной строке и затем перейдите в созданную папку с помощью команды cd lint-examples.</p><h2>Установка ESLint</h2><p>Теперь давайте создадим package.json, чтобы мы могли установить ESLint. Выполнение команды npm init создаст package.json, который необходим для установки eslint в вашу папку.</p><p>Добавьте eslint в ваши npm скрипты</p><p>Подсказка "eslint": "eslint" в скриптах — это сокращение для node_modules/.bin/eslint.</p><h2>Создайте test.js</h2><p>Давайте создадим простой JavaScript-файл в папке lint-example, куда мы установили ESLint. Не переживайте о плохом форматировании примера. Нам нужно это для старта.</p><h2>Первая попытка в командной строке</h2><p>Если вы сейчас запустите test.js, используя ESLint, ничего не случится. По умолчанию ESLint проверяет только синтаксические ошибки. Он будет использовать ES5 по умолчанию. Описание опций парсинга можно найти по <a href="https://eslint.org/docs/user-guide/configuring#specifying-parser-options">ссылке</a>.</p><p>Если вы использовали const или let в примере выше, ESLint генерирует ошибку, потому что, как уже говорилось, ES5 выбран по умолчанию.</p><p>Подсказка Используя -- вы можете передать аргументы через npm-скрипты в командную строку eslint.</p><p>npm run eslint -- ./test.js</p><h2>Становится интереснее!</h2><p>В зависимости от того насколько современен ваш проект, вы должны выставить правильные опции. В наших примерах мы предполагаем, что вы хотите использовать современный ES6 синтаксис.</p><p>Давайте создадим наш первый .eslintrc.</p><p>Существует несколько способов передать конфигурации в ESLint. Я предпочитаю .eslintrc. По <a href="https://eslint.org/docs/user-guide/configuring#configuration-file-formats">ссылке</a> вы найдете другие способы.</p><p>Инфо env обязателен для глобальных переменных. Когда мы настраиваем параметры env, устанавливая es6 в true, ESLint включит глобальность для всех новых типов, таких как Set. Это так же включит ES6 синтаксис, например, let и const. Смотрите <a href="https://eslint.org/docs/user-guide/configuring#specifying-parser-options">установка опций парсера</a>.</p><h2>Сейчас мы должны добавить несколько правил в наш .eslintrc</h2><p>Можем ли мы просто определить правила? Да, потому что установили ESLint, который содержит множество правил из коробки. Для особых правил, таких как TypeScript или новых функций, которые не поддерживаются ESLint, мы должны установить модули eslint-config-xxx или eslint-plugin-xxx. Но мы можем вернуться к этому позже. Вы можете посмотреть правила по <a href="https://eslint.org/docs/rules/">ссылке</a>.</p><p>Если вы запустите npm run eslint, то должны получить результат в точности как ниже:</p><p>Мы сделали большой шаг и теперь знаем, какими должны быть наши стандарты кодирования и форматирования кода, но в реальной жизни правил, конечно же, гораздо больше.</p><p>Возможно, вы увидели в выводе результата ESLint, что 20 проблем из 26 могут быть решены автоматически. Мы вернемся к этому в следующем разделе.</p><h2>ESLint и форматирование кода?</h2><p>ESLint может автоматически форматировать ваш код до определенной стадии. Как вы возможно видели в выводе лога, дополнительный флаг --fix может быть использован для форматирования написанного кода, основываясь на правилах eslint. Например, пропущенная точка с запятой может быть добавлена автоматически, а несколько пустых строк подряд будут удалены. Это так же работает для других правил.</p><p>Давайте исправим код, выполнив npm run eslint -- ./ --fix</p><p>Вы видите, что ESLint исправляет не все правила. Оставшиеся три ошибки необходимо поправить вручную, однако остальные ошибки из отчета ESLint (такие как «Пропущенная точка с запятой», «Неправильные отступы», «Множественные пробелы») были исправлены автоматически.</p><p>Обратите внимание Причина, по которой var не может быть исправлена, — в каких-то действиях с контекстом браузера. Вы можете почитать подробнее по <a href="https://github.com/eslint/eslint/issues/9520">ссылке</a>.</p><p>В документации ESLint вы можете найти, какие правила можно активировать с помощью иконки «check mark». Код, который может быть отформатирован автоматически, подсвечивается с помощью иконки гаечного ключа.</p><ul><li>Правила можно найти по <a href="https://eslint.org/docs/rules/">ссылке</a>.</li><li>Существует примерно 300 правил, и их число постоянно растет.</li><li>Примерно 100 из этих правил делают автоформатирование.</li></ul><p>Всё это становится мощнее, если код форматируется вашей IDE при изменении (сохранении) файла, или если любой инструмент автоматизации, например travis-ci, может взять на себя эту задачу, когда что-то отправляется в Git.</p><h2>Если ESLint может форматировать ваш код, что тогда делает Prettier?</h2><p>Похоже, что ESLint хорошо выполняет некоторое форматирование кода. Как видно в примере выше, этого не достаточно. Код всё ещё выглядит недостаточно хорошо, особенно если вы посмотрите функции. Вот где зерна отделяются от плевел. ESLint прежде всего предназначен для качества кода. Prettier, как видно из названия, делает ваш код красивым. Давайте посмотрим, к чему приведет использование Prettier.</p><h2>Что надо сделать, чтобы начать использовать Prettier?</h2><p>Не так много. Просто добавьте в ваши npm-скрипты "prettier": "prettier" и запустите npm install prettier.</p><p>Как мы помним, этот код был отформатирован ESLint, и он не очень хорошо оформлен.</p><p>После выполнения npm run prettier -- --write ./test.js код выглядит опрятнее.</p><p>Так намного лучше. Чем больше кода, тем лучше результат.</p><h2>Могу ли я настраивать Prettier?</h2><p>Да. Настроек в парсере Prettier не так много, как в ESLint. С Prettier вы полностью во власти парсера Prettier. Основываясь на небольшом количестве опций, он сам решает, как будет выглядеть ваш код.</p><p>Это мои настройки, которые описаны в файле .prettierrc. Полный список опций вы можете найти по <a href="https://prettier.io/docs/en/options.html">ссылке</a>. Давайте создадим .prettierrc-файл с такими опциями.</p><h2>Запускать ли ESLint и Prettier одновременно?</h2><p>Не рекомендуется запускать ESLint и Prettier по отдельности, чтобы применить правила написания кода и форматирования. Более того, ESLint и Prettier будут мешать друг другу т.к. у них есть пересекающиеся правила, и это может привести к непредсказуемым последствиям. В следующем разделе мы рассмотрим эту проблему и решим ее. Если кратко, то вы просто запускаете eslint в командной строке, а prettier будет уже включаться туда.</p><h2>Как всё это начиналось!</h2><p>Как я писал в начале статьи, я никогда не использовал ESLint и Prettier прежде. Следовательно, я не знал, как эти утилиты работают. Как любой разработчик, я копировал наилучший кусок кода из глубин интернета в мой .eslintrc-файл без понимания, что это даст. Главной целью было, чтобы это работало.</p><p>Вот небольшой кусочек кода из моего .eslintrc-файла, который я скопировал из нескольких источников и адаптировал под себя, постепенно понимая, что делает конфиг.</p><p>Если коротко, то там настройки и плагины для ESLint, предоставленные сообществом открытого исходного кода. Мы не должны делать их сами. Главное понимать, как это работает под капотом.</p><p>.eslintrc</p><p>Заметка Возможно, вы заметили prettier в плагинах, и вы все еще помните, что я писал выше: «Должны ли мы одновременно запускать ESLint и Prettier для форматирования кода?» Ответ нет, потому что eslint-plulgin-prettier и eslint-config-prettier сделают всю работу за вас.</p><h2>Что означают эти настройки и опции?</h2><p>После того, как я заставил систему работать, то задался вопросом, а что это всё значит. Это буквально выбило меня из колеи. Если вы запустите ESLint в вашей консоли с этими опциями, то получите сообщение об ошибке, что конфига (расширения) и плагины не установлены. Но откуда мы можем знать, что устанавливать? Каждый знаком с процессом, вы находите кусок кода на StackOverflow или в каком-то репозитории и потом не знаете, как правильно запустить его.</p><p>Вы должны помнить, что все модули внутри extends и plugins могут быть установлены. Но сначала вы должны узнать, как интерпретировать соглашение об именах в свойствах, чтобы иметь возможность устанавливать их через npm.</p><h2>Что такое опции «плагина»?</h2><p>Плагины содержат правила написанные с использованием <a href="https://eslint.org/docs/developer-guide/working-with-custom-parsers">парсера</a>. Это могут быть правила на рассмотрении из <a href="https://github.com/tc39/proposals">TC39</a>, которые еще не поддерживаются ESLint, или специальные рекомендации по написанию кода, которые не представлены в ESLint, например, <a href="https://github.com/sindresorhus/eslint-plugin-unicorn/blob/master/docs/rules/better-regex.md">unicorn/better-regex</a>, <a href="https://github.com/benmosher/eslint-plugin-import/blob/master/docs/rules/no-self-import.md">import/no-self-import</a>.</p><p>Представьте, что вы хотите ввести правило, которое гласит, что в начале файла перед написанием какой-либо строки кода всегда должен быть комментарий, начинающийся со смайлика. Звучит странно, но вы можете сделать это с помощью ESLint Plugin.</p><p>// :penguin: emoji</p><h2>Давайте узнаем, как интерпретировать соглашение об именах плагинов</h2><p>Если имя плагина начинается не с eslint-plugin- или @ или ./, вы просто должны добавить префикс eslint-plugin-.</p><p>Еще пример, работает так же:</p><p>Становится немного сложнее, когда вы сталкиваетесь с именами плагинов, которые начинаются с @ (пространство имен). Как видно из приведенного ниже примера, использование / ограничено одним уровнем. Вы должны учитывать, что @mylint и @mylint/foo находятся в одном и том же пространстве имен, но это два разных плагина (npm-модуля).</p><p>Код примера ниже такой же, как и сверху.</p><p>Используйте сокращенную форму (из первого примера) вместо длинной формы (из второго примера). Главное, чтобы вы понимали, как ESLint это конвертирует внутри.</p><p>Теперь мы знаем, как работает соглашение об именах для плагинов. Установите следующие плагины ESLint через npm.</p><p>npm i eslint-plugin-prettier eslint-plugin-unicorn</p><p>В <a href="https://eslint.org/docs/user-guide/configuring#naming-convention">документации</a> ESLint вы можете найти дополнительную информацию о соглашении об именах.</p><p>Для тестирования ваш .eslintrc должен выглядеть следующим образом:</p><p><a href="https://github.com/prettier/eslint-plugin-prettier">Prettier</a>: ESLint плагин для форматирования кода.</p><p><a href="https://github.com/sindresorhus/eslint-plugin-unicorn#rules">Unicorn</a>: дополнительные правила, которые не поддерживаются ESLint.</p><p>Теперь, если вы запустите npm run eslint в командной строке, вы не получите сообщение об ошибке, но также не получите и вывод ESLint. Это потому, что мы должны зарегистрировать модуль плагина в свойстве extends нашего .eslintrc-файла или применить его, активировав в разделе rules.</p><h2>Давайте выясним, как интерпретировать соглашение об именах в расширениях</h2><p>Прежде всего, если вы считаете, что соглашение об именах в разделе extends такое же, как и у плагинов, я должен вас разочаровать. Есть отличия. Должен честно признать, что мне потребовалось много времени, чтобы заметить разницу. Это было отчасти потому, что ESLint — сложная и обширная тема, по крайней мере, для меня.</p><p>Пока вы используете только простое имя (например, foo) без префикса пространства имен (@) или с (./to/my/config.js), принцип соглашений об именах в extends такой же, как и с параметром plugins. Таким образом, foo становится eslint-config-foo</p><p>идентичен</p><p>Итак, мы подошли к тому, что существуют различия в соглашении об именах между plugins и extends. Это тот случай, когда вы используете пространства имен (@) в разделе extends. Следующая конфигурация ESLint @mylint все та же, она указывает на модуль npm @mylint/eslint-config, но @mylint/foo может привести к ошибке при использовании в extends из-за отсутствия префикса eslint-config- в @mylint/eslint-config-foo.</p><p>Как я писал в введении к предыдущему разделу, следующий @mylint/my-config немного особенный, поскольку он содержит модуль npm, но в то же время он указывает изнутри ESLint на набор правил (my-config). Мы скоро это выясним. Вот официальная <a href="https://eslint.org/docs/developer-guide/shareable-configs">документация</a> по соглашению об именах extends.</p><p>Давайте установим остальные модули npm для нашего примера.</p><p>npm i eslint-config-airbnb-base eslint-config-prettier</p><p>Примечание: возможно, вы заметили, что сначала мы установили eslint-plugin-prettier, а теперь установили eslint-config-prettier. Это разные модули, но работают только вместе. Мы обсудим это позже.</p><h2>Что конкретно делает extends в .eslintrc?</h2><p>Конфиг предоставляет предварительно настроенные правила. Эти правила могут состоять из правил ESLint, правил сторонних плагинов или других <a href="https://eslint.org/docs/user-guide/configuring">конфигураций</a>, таких как синтаксический анализатор (babel, esprima, и т.д.), параметров (sourceType, и т.д.), окружений (ES6, и т.д.) и других.</p><p>Звучит неплохо? Да, потому что мы не должны делать всё сами. Продвинутые разработчики и команды уже потратили на это много времени. Всё, что нужно сделать, это активировать правила, указав конфиг или набор правил плагинов.</p><h2>Где я могу найти эти наборы правил?</h2><p>Есть разные способы их найти.</p><p>Во-первых, вы должны посмотреть на README.md соответствующего репозитория и прочитать именно то, что написано. Обычно, эти наборы правил называются «рекомендованными» и должны быть активированы для plugins. Для extends это не всегда необходимо.</p><p>Во-вторых, знание того, какой набор правил использовать даже без чтения README.md — вот, что я считаю гораздо более эффективным. Особенно, если файл README.md является неполным или неправильным.</p><p>По сути, вы можете сказать, что «плагины» указывают на один файл, где конфигурации (наборы правил), содержащиеся в объекте и «расширениях», указывают на наборы правил, которые находятся в разных файлах.</p><h2>eslint-config-airbnb-base</h2><p>Вы можете активировать все конфигурации одновременно, но будьте осторожны. Вы должны заранее знать, что они делают. Обычно я предварительно изучаю связанные README.md файлы или непосредственно соответствующие наборы правил конфигурации. Довольно легко, когда вы поймете, как их активировать, верно?</p><h2>Использование:</h2><p>Держите в уме Порядок играет роль, потому что набор правил будет расширять или перезаписывать предыдущие. Так что не переусердствуйте с конфигами и плагинами. Смотрите хорошее объяснение на <a href="https://stackoverflow.com/questions/46544082/it-this-the-correct-way-of-extending-eslint-rules#50370083">StackOverflow</a>.</p><h2>eslint-plugin-prettier</h2><p>Теперь мы подошли к захватывающей части статьи. Как мы можем использовать Prettier напрямую в ESLint, не запуская его в качестве отдельной службы в нашей командной строке или IDE?</p><p>Мы начнём с активации eslint-plugin-prettier в разделе extends, а затем связанного с ним config eslint-config-prettier, который отвечает за деактивацию некоторых наборов правил ESLint, которые могут конфликтовать с Prettier.</p><p>eslint-plugin-prettier.js</p><h2>Использование:</h2><p>Подсказка Плагины должны быть зарегистрированы в plugins и активированы в extends с использованием :plugin префикс.</p><h2>eslint-config-prettier</h2><h2>Использование:</h2><p>Примечание Отдельно "prettier" в extends требуется для отключения некоторых правил ядра ESLint. В остальных случаях "prettier." необходимы для отключения правил в unicorn и @typescript-eslint.</p><p>Мой личный конфиг ESLint выглядит как приведенный выше пример. Я использую TypeScript и плагин Unicorn. Не хочу, чтобы они конфликтовали с ESLint. Поэтому некоторые правила TypeScript и Unicorn отключены через Prettier.</p><p>Ранее мы активировали наборы правил, которые внутренне представляют собой не что иное, как сгруппированные правила, но вам не нужно использовать комбинированный набор правил. Вы также можете изменить или отключить отдельные правила.</p><p>Активировать всё самостоятельно вместо использования набора правил не имеет смысла. Но часто случается так, что вы не согласны с тем или иным правилом или его настройками. В этом случае вы можете отключить одно правило.</p><h2>.eslintrc</h2><p>Итак, вернёемся к нашему тестовому примеру. Теперь наш .eslintrc-файл должен выглядеть следующим образом:</p><p>Стратегия При переходе на ESLint может случиться так, что в выводе ESLint отображается много ошибок. Правка по ходу дела может занять много времени или даже привести к побочным эффектам. Если вы хотите переходить постепенно, рекомендуется оставить <a href="https://eslint.org/docs/user-guide/configuring#configuring-rules">правила</a> в режиме warning, а не error.</p><p>Если вы теперь запустите наш пример через ESLint, используя npm run eslint -- --fix, Prettier будет выполняться через ESLint, так что вам потребуется только одна команда для запуска обоих инструментов.</p><h2>Как мы можем интегрировать это в IDE?</h2><p>Все современные IDE (IntelliJ и VS Code) поддерживают ESLint. Важно отметить, что в некоторых случаях вы должны передавать параметр --fix в качестве аргумента в настройках IDE, чтобы всё работало автоматически.</p><h2>Почему существуют разные типы парсеров “ESLint”?</h2><p>ESLint поддерживает только новый синтаксис JavaScript, который находится на финальной стадии в <a href="https://github.com/eslint/eslint#what-about-experimental-features">TC39</a>. Возможно, не многие знают это, но компилятор Babel поддерживает функции, которые ещё не находятся на финальной стадии. Хорошо известная функция – decorator. От функции, на которой был основан Angular, отказались. Новая же функция имеет другой синтаксис и семантику. Предыдущая функция находится на второй стадии, а новая — на раннем этапе.</p><p>В этом случае ESLint вам не поможет. Либо вам нужно найти подходящий плагин для него, либо вы пишете свой собственный плагин eslint, который использует, например, анализатор babel вместо анализатора espree, который является анализатором по умолчанию в ESLint.</p><p>Смотрите настройки <a href="https://eslint.org/docs/user-guide/configuring#specifying-parser">eslint-parser</a>.</p><h2>Как насчёт Angular и ESLint?</h2><p>Команда Angular придерживается мнения, что нам следует подождать с применением ESLint. Это допустимо, потому что они хотят сделать переход как можно более плавным, но если вы всё же хотите попробовать, вот несколько <a href="https://github.com/angular/angular-cli/issues/13732#issuecomment-617274183">советов</a>.</p><h2>Производительность и ESLint?</h2><p>Может случиться, что ESLint не так эффективен, как можно было бы ожидать в некоторых частях кода, но это нормально и может произойти также в TSLint. Для решения проблемы вы можете использовать внутреннее кэширование ESLint или другого демона ESLint. Здесь вы можете найти очень полезные советы, см. пост на <a href="https://stackoverflow.com/questions/38458067/which-eslint-rules-in-my-config-are-slow#55340763">StackOverflow</a>.</p><h2>Prettier существует только для Javascript?</h2><p>Prettier <a href="https://prettier.io/docs/en/plugins.html#official-plugins">официально поддерживает</a> несколько других языков. К ним относятся PHP, Ruby, Swift и так далее. Кроме того, существуют <a href="https://prettier.io/docs/en/plugins.html#community-plugins">плагины сообщества</a> для таких языков как Java, Kotlin, Svelte и многих других.</p><h2>Как насчет ESLint v7?</h2><p>Все примеры в нашей статье изначально были основаны на версии ESLint v6, но недавно была выпущена версия ESLint v7. Не волнуйтесь, даже в версии 7 ESLint работает без каких-либо изменений. Если вас интересует, что было изменено или добавлено, вы можете ознакомиться с <a href="https://eslint.org/blog/2020/05/eslint-v7.0.0-released">примечаниями к выпуску</a> ESLint v7.</p>]]></content:encoded>
    </item>
    <item>
      <title>Логирование как инструмент повышения стабильности веб-приложения</title>
      <link>https://tproger.ru/articles/logging-on-frontend-and-backend</link>
      <comments>https://tproger.ru/articles/logging-on-frontend-and-backend?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/logging-on-frontend-and-backend</guid>
      <description><![CDATA[<p>Логи содержат системную информацию, помогают отлаживать части приложения, анализировать работу системы и быстрее выявлять ошибки после релиза.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/logging-on-frontend-and-backend">Логирование как инструмент повышения стабильности веб-приложения</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 06 May 2020 07:20:48 GMT</pubDate>
      <content:encoded><![CDATA[<p>Каждый проект так или иначе имеет жизненные циклы: планирование, разработка MVP, тестирование, доработка функциональности и поддержка. Скорость роста проектов может отличаться, но при этом желание не сбавлять обороты и двигаться только вперёд у всех одинаковые. Перед нами встаёт вопрос: как при работе над крупным проектом минимизировать время на выявление, отладку и устранение ошибок и при этом не потерять в качество?</p><p>Существует много различных инструментов для повышения стабильности проекта:</p><ul><li>статические анализаторы (ESLint, TSLint, Pylint и др.);</li><li>контейнеризация (Docker, Vagrant и др.);</li><li>различные виды тестирования (функциональное тестирование, тестирование производительности, системное тестирование, модульное тестирование, тестирование безопасности);</li><li>менеджеры зависимостей (npm, yarn, pip и др.);</li><li>логирование + мониторинг;</li><li>менеджеры процессов;</li><li>системные менеджеры.</li></ul><p>В данной статье я хочу поговорить об одном из таких инструментов — логировании.</p><p>Логи — это файлы, содержащие системную информацию о работе сервера или любой другой программы, в которые вносятся определённые действия пользователя или программы.</p><p>Логи полезны для отладки различных частей приложения, а также для сбора и анализа информации о работе системы с целью выявления ошибок. Всё это необходимо для контроля работы приложения, так как даже после релиза могут встретиться ошибки, а пользователи не всегда сообщают о багах в техподдержку. Чем больше процессов у вас автоматизировано, тем быстрее будет идти разработка.</p><p>Допустим, есть клиентское приложение, балансировщик в лице Nginx, серверное приложение и база данных.</p><figure><img src="https://media.tproger.ru/uploads/2020/05/image3.jpg" alt="" /></figure><p>В данном примере не важны язык/фреймворк бэкенда, фронтенда или тип базы данных, а вот про веб-сервер Nginx давайте поговорим. В данный момент Nginx популярнее остальных решений для высоконагруженных сайтов. Среди известных проектов, использующих Nginx: Рамблер, Яндекс, ВКонтакте, Facebook, Netflix, Instagram, Mail.ru и многие другие. Nginx записывает логи по умолчанию, без каких-либо дополнительных настроек.</p><p>Логи доступны 2 типов:</p><ul><li>логи ошибок (logs/error.log) — хранят запросы, которые завершились с ошибкой;</li><li>логи доступа (logs/access.log) — хранят информацию обо всех запросах, которые были отправлены на сервер.</li></ul><p>Клиент отправляет запрос на сервер, и в данной ситуации Nginx будет записывать все входящие запросы. Если возникнут ошибки при обработке запросов, сервером будет записана ошибка.</p><p>2020/04/10 13:20:49 [error] 4891#4891: *25197 connect() failed (111: Connection refused) while connecting to upstream, client: 5.139.64.242, server: app.dunice-testing.com, request: "GET /api/v1/users/levels HTTP/2.0", upstream: "http://127.0.0.1:5000/api/v1/users/levels", host: "app.dunice-testing.com"</p><p>Всё, что мы смогли бы узнать в случае возникновения ошибки, — это лишь факт наличия таковой, не более. Это полезная информация, но мы пойдём дальше. В данной ситуации помог Nginx и его настройки по умолчанию. Но что же нужно сделать, чтобы решить проблему раз и навсегда? Необходимо настроить логирование на сервере, так как он является общей точкой для всех клиентов и имеет доступ к базе данных.</p><p>Первым делом каждый запрос должен получать свой уникальный идентификатор, что поможет отличить его от других запросов. Для этого используем UUID/v4. На случай возникновения ошибки, каждый обработчик запроса на сервере должен иметь обёртку, которая отловит эти самые ошибки.  В этой ситуации может помочь конструкция try/catch, реализация которой есть в большинстве языков.</p><p>В конце каждого запроса должен сохраняться лог об успешной обработке запроса или, если произошла ошибка, сервер должен обработать её и записать следующие данные: ID запроса, все заголовки, тело запроса, параметры запроса, отметку времени и информацию об ошибке (имя, сообщение, трассировка стека).</p><p>Собранная информация даст не только понимание, где произошла ошибка, но и возможную причину её возникновения. Обычно для решения ошибки информации из лога достаточно, но в некоторых случаях может быть полезен контекст запроса. Для этого необходимо при старте запроса не только генерировать ID запроса, но и сгенерировать контекст, в который мы будем записывать всю информацию по работе сервера, начиная от результата вызова функции и заканчивая результатом запроса к базе данных. Такая реализация даст не только входные данные, но и промежуточные результаты работы сервера, что позволит понять причину появления ошибки.</p><figure><img src="https://media.tproger.ru/uploads/2020/05/image2.jpg" alt="" /></figure><p>При микросервисном подходе система не ограничивается одним сервером, и при запросе от клиента происходит взаимодействие нескольких серверов внутри системы. Наша реализация логирования на сервере позволит выявить дефект в работе конкретного ресурса, но не позволит понять, почему запрос вернулся с ошибкой. В данной ситуации поможет трассировка запросов.</p><p>Трассировка — процесс пошагового выполнения программы. В режиме трассировки программист видит последовательность выполнения команд и значения переменных на каждом шаге выполнения программы.</p><p>В нашем случае требуется передавать метаинформацию о запросе при взаимодействии серверов и записывать логи в единое хранилище (такими могут быть ClickHouse, Apache Cassandra или MongoDB). Такой подход позволит привязать различные контексты серверов к уникальному идентификатору запроса, а отметки времени — понять последовательность и последнюю выполненную операцию. После этого команда разработки сможет приступить к устранению.</p><figure><img src="https://media.tproger.ru/uploads/2020/05/image4.jpg" alt="" /></figure><p>В некоторых случаях, которые встречаются крайне редко, к ошибке приводят неочевидные факторы: компилятор, ядро операционной системы, конфигурации сервера, юзабилити, сеть. В таких случаях при возникновении ошибки потребуется дополнительно сохранять переменные окружения, слепок оперативной памяти и дамп базы. Такие случаи настолько редки, что не стоит беспочвенно акцентировать на них внимание.</p><p>С сервером разобрались, что же делать, если у нас сбои даёт клиент и запросы просто не приходят? В такой ситуации нам помогут логи на стороне клиента. Все обработчики должны отправлять информацию на сервер с пометкой, что ошибка с клиента, а также общие сведения: версия и тип браузера, тип устройства и версия операционной системы. Данная информация позволит понять, какой участок кода дал сбой и в каком окружении пользователь взаимодействовал с информацией.</p><figure><img src="https://media.tproger.ru/uploads/2020/05/image1.jpg" alt="" /></figure><p>Также есть возможность отправлять уведомления на почту разработчикам, если произошли ошибки, что позволит оперативно узнавать о сбоях в системе. Такие подходы активно используются в системах мониторинга и аналитики логов.</p><p>Способы, которые мы рассмотрели в статье, помогут следить за качеством продукта и минимизируют затраты на исправление недочётов в системе.</p>]]></content:encoded>
    </item>
    <item>
      <title>Продвинутый дебаг в Xcode: средства отладки, про которые часто забывают</title>
      <link>https://tproger.ru/articles/advanced-debuggin-in-xcode</link>
      <comments>https://tproger.ru/articles/advanced-debuggin-in-xcode?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/advanced-debuggin-in-xcode</guid>
      <description><![CDATA[<p>Senior iOS-разработчик Noveo напоминает о доступных из коробки техниках: продвинутые брейкпоинты, влияние на состояние приложения, правка UI.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/advanced-debuggin-in-xcode">Продвинутый дебаг в Xcode: средства отладки, про которые часто забывают</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Мобильная разработка]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[iOS]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 13 Apr 2020 13:09:06 GMT</pubDate>
      <content:encoded><![CDATA[<p>Senior iOS-разработчик Noveo напоминает о доступных из коробки, но часто игнорируемых разработчиками средствах отладки кода в среде Xcode: продвинутое использование брейкпоинтов, влияние на состояние приложения, редактирование UI без перезагрузки и другие техники, которые помогут ускорить отладку приложения или поиск багов.</p><h2>Intro</h2><p>Каждый разработчик, независимо от квалификации и типа текущей задачи, постоянно находится в знакомом всем цикле: мы пишем код, запускаем и исправляем. Количество итераций у каждого разное, но делаем мы это ежедневно множество раз.</p><p>По данным некоторых исследований, мы в среднем тратим до 60% времени на отладку — и это именно усредненное значение, для кого-то, особенно для начинающих разработчиков, оно может быть ещё больше. Пост призван уменьшить это время и сделать процесс отладки эффективнее и приятнее.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Kod-iznachalnyj-kod_2x.png" alt="" /></figure><p>Давайте посмотрим на процесс изнутри. Я привёл немного странный пример, но он достаточно связан с ежедневной рутиной и отлично опишет большинство кейсов. Этот код вычисляет высоту ячейки таблицы. Высота зависит от некоторых констант и параметров. Допустим, в некоторых случаях высота рассчитывается неверно. Наша задача — изучить эту функцию, например узнать значение флага showTitle в момент вычисления. Что первым приходит на ум? Правильно, поместить print() для отладки.</p><p>Запускаем проект — он у нас большой, да и Xcode неидеальный. Чаще всего происходит всем знакомая ситуация: добавили одну строчку и ждём минуту, пока соберётся. А ведь кроме сборки и запуска нужно ещё и восстановить условия, добраться до нужного экрана, воспроизвести проблему и только после этого посмотреть вывод свежедобавленного print‘a.</p><p>Как это обычно бывает, с первого раза расставить print‘ы в полезных местах довольно трудно. Так произошло и в этот раз. Само по себе знание о состояниии флага нам почти ни о чём не говорит, поэтому было решено добавить ещё один print с информацией об элементе, для которого производится расчёт. Затем мы пожелали переопределить некоторые значения констант и сделать исключение для одного элемента.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Kod-posle-jeksprerimentov_2x.png" alt="" /></figure><p>И снова нас ждёт сборка, воспроизведение условий и анализ. Мне кажется, все через это проходили и, надеюсь, это — притянутая за уши история, существующая лишь как вводная для этой статьи. Справедливости ради замечу, что отладка через print’ы сама по себе не является чем-то плохим. Порой это единственный способ отладить что-то в сложных ситуациях, например когда ошибки происходят в оптимизированном компилятором коде. Но сегодня разговор пойдёт о простых сценариях, которые чаще всего происходят во время разработки.</p><h2>Breakpoints</h2><p>Мы уже выяснили, что отладка через добавление вывода в консоль не совсем эффективна и нам нужен какой-то инструмент, который облегчит нам жизнь. Таким инструментом являются брейкпоинты. Все мы любим этот механизм, который представлен практически в любой среде разработки на любых платформах и языках, и пользуемся им. Где-то он реализован лучше, где-то хуже, но в целом Apple предоставила нам мощный и гибкий механизм точек останова. Однако, работая с разными людьми, я заметил, что пользуются ими в большинстве случаев исключительно для остановки программы — просто чтобы убедиться, что её выполнение пошло по запланированному сценарию. Иногда люди пользуются консолью отладки и командой po, но лишь в тех случаях, когда нужно разок выяснить состояние переменной. Я предлагаю рассмотреть дополнительные возможности отладчика, встроенного в нашу IDE, и привести примеры ситуаций, в которых они могут пригодиться.</p><h3>Conditional breakpoints</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Okno-redaktirovanija-brejkpointa.png" alt="" /></figure><p>Начнём с очевидного: conditional breakpoints. Как ни странно, строка, позволяющая указать условия срабатывания брейкпоинта, всегда у нас перед глазами, но почему-то люди удивляются такой возможности. Для удивлённых самим наличием такого диалога — его можно увидеть, если дважды нажать на сам брейкпоинт. В эту строку можно записать любое выражение, которое может вернуть булево значение, будь то сравнение переменной из текущей области видимости или вовсе значение какого-то синглтона. Но будьте внимательны — медленно вычисляемое выражение способно существенно снизить производительность вашей программы.</p><h3>Skipping</h3><p>Следующей возможностью, которая тоже обделена вниманием разработчиков, является игнорирование N-го количества срабатываний. Эта возможность может пригодиться, например, в рекурсивных функциях, чтобы посмотреть, что происходит на N-ой глубине, или же посмотреть результат функции для N-го элемента массива. В примере с массивом этот способ будет предпочтительнее установки условия, т.к. не требует вычисления выражения.</p><h3>Actions</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/GIF-nazhatija-na-Add-Action.png" alt="" /></figure><p>Но самое интересное дня нас кроется за кнопкой Add Action. Эта кнопка позволяет добавить дополнительное действие, которое будет вызвано в момент срабатывания брейкпоинта. Как вы видите, есть 6 типов действий, которыми можно дополнить брейкпоинт:</p><ol><li>Apple script. Позволяет запустить скрипт на одноименном языке.</li><li>Capture GPU Frame. Для отладки приложений, использующих движок Metal, может потребоваться эта опция.</li><li>Debugger command. Позволяет выполнить команду отладчика. О ней мы поговорим позже.</li><li>Log-message. Позволяет вывести текстовое сообщение в лог.</li><li>Shell command. Позволяет выполнить произвольную команду в среде, дефолтной для системы командной оболочки, sh/bash/zsh.</li><li>Sound. Позволяет проиграть звук из динамиков компьютера, на котором запущен Xcode.</li></ol><p>Я не буду рассказывать о первых двух типах — они слишком специфичны и вряд ли вам пригодятся. А ещё пропущу последний пункт, так как особо рассказывать там нечего. Но помнить о нём стоит — он может вам пригодиться, например, когда нужно быстро совершить некое действие в приложении вслед за триггером, которым и может являтся звук от брейкпоинта, поставленного в нужное место.</p><h3>Log message</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Redaktirovanie-brejkpointa-s-jekshenom-Log-Message.png" alt="" /></figure><p>Рассмотрим чуть более подробно тип дополнительного действия «Log message».</p><p>Если мы его выберем, к нашим услугам окажется строка ввода формата сообщения. Обратите внимание, что в строке можно указывать полезные плейсхолдеры, два из которых позволяют подставить информацию о брейкпоинте и одно, самое полезное, позволяет подставить результат вычисления произвольного выражения. Таким выражением может быть переменная или любая другая конструкция используемого вами языка программирования. Но это не имеет никакого смысла, если не поставить галочку «Automatically continue after evaluating actions». Именно она в паре с любым из действий позволит нам экономить время на дебаге. Больше не нужно писать print(), пересобирать проект и ждать вечность. В любой момент времени, без перезапуска проекта вам доступен вывод в консоль отладки любой информации о ходе выполнения программы. А для знающих толк в извращениях дебаге Apple предусмотрела возможность воспроизвести выражения, используя встроенный синтезатор речи.</p><h3>Shell command</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Redaktirovanie-brejkpointa-s-jekshenom-shell-command..png" alt="" /></figure><p>Нетрудно догадаться, что этот экшен позволяет запустить произвольную команду в стандартной оболочке терминала ОС. Как и «Log message», она позволяет вычислить результат выражения в текущем контексте и дополнить им аргументы вызова команды. Для чего это может быть полезно? Примеров использования можно придумать массу. Из реальной жизни: запуск троттлинга через Charles. Необходимо было замедлять запросы из определённой точки, при этом в остальное время соединение должно было быть полноценным. Я не успевал включать-выключать троттлинг вручную и ещё совершать действия в симуляторе. Такой трюк с брейкпоинтом и «Shell command» отлично меня выручил. В другой раз мне понадобилось изменять информацию на сервере прямо параллельно с запросом, чтобы отловить довольно странный баг. Тут тоже был кстати этот вид брейкпоинта. Особые извращенцы могут собрать конструкцию на Arduino с электрошокером и бить себя током при каждом срабатывании нежелательного кода. Шучу. Не пытайтесь это воспроизвести в реальной жизни.</p><h3>Debugger command</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Redaktirovanie-brejkpointa-s-jekshenom-Debugger-command.png" alt="" /></figure><p>Одним из самых интересных видов экшенов я считаю «Debugger command». Этот экшен позволяет действительно безгранично влиять на отлаживаемую программу. Debugger command — это команды отладчика LLDB, а LLDB — это отладчик для проекта LLVM, который сейчас используется Apple и Xcode для сборки программ. Отладчик LLDB позволяет подключаться к процессу, прерывать выполнение программы и воздействовать на её память. Для этого отладчик имеет множество команд, некоторые из которых станут героями сегодняшнего повествования. Именно благодаря отладчику LLDB у нас в принципе есть такая замечательная возможность отлаживать программу, в частности устанавливать брейкпоинты. Начнём мы с самой известной команды — po. Наверняка многие из вас уже не раз использовали эту команду при отладке, но для меня в своё время это стало открытием, хотя я уже имел некоторый опыт в разработке под iOS на тот момент. Po — это сокращение от print object. Команда позволяет вычислить выражение из правой части от команды и распечатать в консоли результат выполнения. При этом у объекта запросится его debugDescription, если он определён, или просто description, если нет. У po существует команда-прародитель — print, или p, которая точно так же вычислит выражение и распечатает результат, но только в этом случае вам будет доступна сырая информация об объекте или скалярном типе. Обе эти команды будут компилировать введенное выражение в текущем контексте, что неминуемо замедлит выполнение кода при срабатывании брейкпоинта. К счастью, в Xcode 10.2 Apple добавили ещё одну команду отладчика — v, которая работает значительно быстрее. Она позволяет вывести в консоль значение переменной из текущей области видимости, но, в отличии от p и po, без компиляции выражения. Естественное ограничение, накладываемое этой особенностью, — вывод в консоль возможен только для хранимых свойств.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/exampleimg.png" alt="" /></figure><h3>Affecting execution flow</h3><p>Такая комбинация (брейкпоинт + debugger command po + автоматическое продолжение) заменит нам описанную ранее Log message. Что же ещё мы можем сделать с помощью такой комбинации? Например, с помощью дебаггера мы можем пропустить выполнение нескольких строчек кода, будто они закомментированы. При этом вам не нужно пересобирать программу и заново воспроизводить условия. Для этого достаточно ввести thread jump --by 1 для скачка вперёд на одну строчку или же thread jump --line 44 для перехода, как вы уже могли догадаться, к 44 строчке. Но будьте осторожны — вы не можете на 100% безопасно перепрыгивать по строчкам. Дело в том, что вы можете перепрыгнуть через инициализацию некоторой переменной, и это вызовет краш. Дело осложняется тем, что Swift «ленив» по своей природе, и инициализация может происходить не там, где вам кажется. Плюс компилятор при сборке вашей программы вставляет дополнительные инструкции, например для управления памятью, пропуская которые вы рискуете получить в лучшем случае утечку, в худшем — краш.</p><h3>Affecting debugger</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Redaktirovanie-brejkpointa-s-vvedennoj-komandoj-i-Leo-DiKaprio-iz-mema-We-Need-Go-Deeper-.png" alt="" /></figure><p>Кроме влияния на вашу программу, с помощью отладчика вы можете влиять на сам отладчик. Например, мы можем поставить брейкпоинт из брейкпоинта. Вы спросите, зачем это нужно? Бывают методы общего назначения, которые срабатывают по ряду триггеров. Например функция по отправке сообщения в аналитику может вызываться сотню раз в секунду, а нам нужно отловить именно ту отправку, которую породит нажатие на кнопку. В этом случае мы можем поставить брейкпоинт на метод нажатия кнопки и добавить команду установки брейкпоинта на произвольной строке программы в произвольном файле. Команда bp s -o -f Calc.swift -l 44 расшифровывается как breakpoint set one-shot на файл Calc.swift на строку 44. Модификатор -o или --one-shot создаст специальный тип брейкпоинта, который «живёт» ровно до момента своего срабатывания, а после исчезает. Таким нехитрым способом мы можем создавать интересные алгоритмы установки брейкпоинтов для отладки нетривиальных багов.</p><h3>Other breakpoints types</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Panel-perekljuchenija-vidov-levoj-funkcionalnoj-kolonki-XCode-c-otkrytym-Breakpoint-Navigator-i-neskolkimi-brejkopintami.png" alt="" /></figure><p>А есть ли ещё виды брейкпоинтов, о которых мы можем не знать? Конечно, есть. Xcode позволяет добавить некоторые виды брейкпоинтов, которые не относятся к какому-то конкретному файлу и строке. В Xcode есть вкладка Breakpoint Navigator, которая позволяет управлять уже созданными брейкпоинтами сквозь все файлы проекта, а также создавать новые. Внизу окна нашего IDE есть кнопка со значком плюса.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Nizhnjaja-funkcionalnaja-panel-leovoj-koloki-XCode-pri-otkrytom-Breakpont-Navigator.png" alt="" /></figure><p>Это позволяет использовать 6 дополнительных типов брейкпоинтов:</p><ol><li>Swift Exception брейкпоинт — брейкпоинт, останавливающий программу при срабатывании не перехваченного throw для Swift кода.</li><li>Exception брейкпоинт — то же самое, но для мира ObjC. Может показаться, что это не актуальный в современном мире брейкпоинт, но это не так. Стоит помнить, что нам пока всё ещё нужен UIKit, написанный на ObjC, ошибки которого мы можем отловить с помощью такого вот брейкпоинта.</li><li>Symbolic breakpoint — позволяет останавливать процесс выполнения программы при выполнении кода, ассоциированного с некоторым идентификатором, который Apple называет символом. О символах я расскажу чуть позже.</li><li>OpenGL ES Error брейкпоинт — брейкпоинт, останавливающий программу при возникновении ошибки OpenGL при разработке соответствующих приложений.</li><li>Constraint Error breakpoint — очевидно, остановит вашу программу при возникновении ошибки автолейаута.</li><li>Test Failure breakpoint может вам помочь при отладке тестов.</li></ol><p>Так как уместить в этой сессии обзор всех типов точек останова не представляется возможным, я остановлюсь только на самых часто используемых. По своему опыту — я всегда использую Exception breakpoint. Довольно часто при разработке программ я сталкиваюсь с перехваченными системными исключениями, отладить которые порой проблематично из-за крайне неинформативного call stack’а. Думаю, вы сталкивались хоть раз с такой или подобной ошибкой:</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Soobshhenie-v-Debugger-Console-pri-padenii-prilozhenija-iz-za-ne-perehvachennogo-iskljuchenija-ObjectiveC.png" alt="" /></figure><h3>Exception breakpoint</h3><p>Для того, чтобы сделать стек вызова более информативным, мы можем добавить Exception breakpoint. Он позволит остановить программу прямо на моменте выброса исключения и отследить цепочку событий, которые привели к такому результату. По умолчанию неперехваченное исключение вызовет аварийную остановку приложения, и в стеке вызова мы ничего полезного не увидим, т.к. исключение будет пробрасываться вверх по стеку вызова и вся информация о месте выброса будет утеряна. Exception breakpoint позволяет остановить программу в момент выброса исключения и уже привычными нами методами получить гораздо больше информации о проблеме, пройдясь по стеку вызова и просмотрев значения переменных, если это необходимо. Я считаю этот тип брейкпоинта очень полезным и использую его на всех проектах по умолчанию. Для этого в Xcode есть удобный механизм, который позволяет указать брейкпоинту уровень и хранить его на трёх уровнях:</p><ol><li>Проект.</li><li>Воркспейс.</li><li>Пользователь.</li></ol><p>Просто нажмите на брейкпоинт правой кнопкой мыши и выберите Move breakpoint. Перенесённый на уровень пользователя, брейкпоинт будет доступен на всех проектах, какой бы вы ни открыли в вашем Xcode.</p><h3>Symbolic Breakpoint</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Okno-dobavlenija-Symbolic-Breakpoint.png" alt="" /></figure><p>Вторым часто используемым типом брейкпоинтов является Symbolic Breakpoint. Ранее я уже писал, что этот брейкпоинт позволяет останавливать программу при выполнении кода, ассоциированного с каким-то символом, и обещал рассказать подробнее про символы. Так вот, символы — это человекопонятные идентификаторы, которые ассоциируются с тем или иным адресом в памяти. LLDB умеет маппить известные ей символы в адреса функций и наоборот. При каждой сборке проекта система создаёт особый бандл из специальных файлов в формате dSYM, которые расшифровываются как Debug Symbols. Эти файлы хранят что-то вроде таблицы, содержащей в себе некоторые адреса методов и некоторые идентификаторы, среди которых сигнатуры методов, имена файлов, смещения и номера строк. Именно благодаря этим файлам мы можем поставить брейкпоинт на строку файла, получить читаемый стек вызова или расшифровать crashlog приложения из AppStore.</p><p>Благодаря этому механизму мы можем поставить брейкпоинт на любом методе класса, зная только его название. При этом нам не нужно достоверно знать, где этот метод объявлен и доступны ли вообще нам исходные файлы. Давайте рассмотрим реальный пример. Вас перевели на новый проект, и первая задача — исправить непонятное поведение на форме ввода данных кредитной карты, когда посреди набора фокус вдруг перепрыгивал на поле ввода имени. Сходу ничего не понятно, кода много, но симптомы ясны. Для расследования необходимо понять, кто и почему инициирует смену фокуса. Можно долго читать код, искать логику в неочевидных расширениях классов, а как надоест — сделать наследника UITextField, переопределив там метод becomeFirstResponder(), поменять реализации и уже там поставить брейкпоинт. А можно за 10 секунд создать символьный брейкпоинт -[UITextField becomeFirstResponder], и программа остановится в момент смены фокуса. По цепочке бэктрейса мы сможем легко восстановить последовательность событий, которые приводят к нежелательным результатам.</p><p>У тех, кто пользуется таким видом брейкпоинта в первый раз, наверняка возник вопрос: а что это за символ -[UITextField becomeFirstResponder]? Это ObjectiveC-сигнатура метода установки текста для лейбла. Использование ObjectiveC обусловлено тем, что UIKit написан именно на этом языке. Пара слов для тех, кто имел мало опыта с ObjectiveC. Знак минуса обозначает, что нас интересует инстанс-метод, а не метод класса, далее в квадратных скобках записывается название класса и через пробел метод, двоеточие указывает на то, что этот метод принимает параметр. Тут можно возразить, что пример притянут за уши. Я согласен — в хорошем коде не будет десятка мест с установкой текста лейбла, но моя цель — показать, как это может работать. Давайте рассмотрим более реальный пример. Допустим, для целей отладки нам может понадобиться распечатать последовательность показа вью контроллеров. Добавляем брейкпоинт с символом -[UIViewController viewDidAppear:], указываем дополнительное действие po NSStringFromClass([instance class]) и, конечно же, не забываем поставить галочку «Automatically continue after evaluating actions».</p><p>Мы снова вынуждены использовать ObjC, даже в дополнительной команде, так как находимся в его контексте. Что касается Swift, то символы записываются как название ClassName.methodName(param:). Прописывать параметры не обязательно, LLDB попытается разрешить неоднозначность, если есть методы с одинаковым названием, но разными параметрами.</p><p>Рассказывая о символьных брейкпоинтах, я не могу не рассказать о возможности искать символы. Остановив программу любым способом, с помощью брейкпоинта или же просто нажав на пиктограмму паузы, мы можем воспользоваться командой image lookup -r -n и найти интересующие вас символы в вашей программе и во всех загруженных библиотеках. Это действительно делает вас чуть ли не богом дебага, потому как вы властны искать символы везде, скажем в UIKit’e, искать приватные методы, останавливать и изучать внутреннее устройство системных библиотек. Надеюсь, я убедил в вас в силе этого метода и он не раз поможет вам сэкономить время.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Poisk-privatnogo-metoda-v-UIKite-c-pomoshhju-image-lookup.png" alt="" /></figure><h3>Watchpoints</h3><p>Вотчпоинты позволяют останавливать программу, когда изменяется значение переменной. Корректнее будет сказать, что этот механизм позволяет следить за изменениями памяти по заданному адресу с заданным размером, но благодаря LLDB и Xcode разработчику достаточно сделать несколько кликов. Использование вотчпоинтов будет удобным, когда за изменением переменной не следует никакого сайд-эффекта прямо после изменения, но её состояние важно для отложенных вычислений. В ряде случаев может быть непонятно, что инициирует это изменение, и вотчпоинты позволят быстро узнать это. Достаточно приостановить выполнение программы в контексте нужного класса и воспользоваться окном Variables View. Тут будут перечислены переменные в текущем фрейме, доступные к отлаживанию. В крупных проектах вычисление доступных переменных и их типов может занимать некоторое время, поэтому иногда нужно подождать несколько (десятков?) секунд перед тем, как переменные будут доступны к манипуляциям над ними. Приятным бонусом является возможность «заглянуть» внутрь объектов Objective-C: функциональность Variables View позволяет увидеть приватные переменные этих объектов. По клику правой кнопки мыши по переменной нам доступно не так много опций — мы можем изменять значение переменных скалярных типов и, собственно, добавлять вотчпоинты.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Levaja-kolonka-DebuggerView-s-kontekstnym-menju-po-odnoj-iz-peremennyh..png" alt="" /></figure><p>Конечно же, вотчпоинт можно установить и командой LLDB: watchpoint set variable &lt;variable_name&gt;, или, пользуясь функцией сокращения команд LLDB, просто: w s v &lt;variable_name&gt;, но помните, что переменная должна быть видна отладчику, то есть находиться в текущем фрейме. Помимо установки брейкпоинта на изменение переменной, нам доступна установка вотчпоинта на область памяти: watchpoint set expression -- 0x0d78ab5ea8. В обоих случаях при изменении содержимого памяти по отслеживаемому адресу произойдет прерывание программы. Установленные точки останова можно посмотреть командой watchpoint list или в Debugger navigator. Так как любые вотчпоинты в итоге следят за адресом памяти, они становятся неактуальны после перезапуска и не сохраняются между перезапусками приложения. Даже если вы установили брейкпоинт на изменение переменной, под капотом механизм lldb вычислил её адрес и поставил вотчпоинт по этому адресу.</p><h3>Affecting state</h3><p>Будем закругляться. Последнее, о чем я хотел поведать в рамках этой статьи, — влияние на состояние приложения из LLDB. До этого я говорил только об изменении состояния какого-либо объекта системы при остановке по брейкпоинту. Но что, если нам требуется приостановить программу в произвольный момент времени? Нажатие на значок паузы приводит к приостановке программы, но вот вместо привычного нам кода мы увидим код ассемблера. Так как же добраться до произвольного объекта и выполнить с ним хитрые манипуляции?</p><h3>Memory graph</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Panel-instrumentov-otladchika-Xcode-s-podsvechennoj-knopkoj-Memory-Graph.png" alt="" /></figure><p>Большинство iOS-разработчиков уже с первых месяцев своей работы используют этот инструмент. Для тех, кто ни разу им не пользовался, поясню. Memory graph позволяет сделать дамп памяти программы и отобразить в виде списка и графа все экземпляры объектов, которые сейчас находятся в памяти. Зачастую этот инструмент используется для выявления утечек объектов и анализа связей, которые привели к такому результату. Но сегодня от этого инструмента нам нужна только возможность остановить программу в произвольное время, найти нужный объект и узнать его адрес. Но что мы можем сделать с этой, казалось бы, бесполезной информацией?</p><p>На самом деле — всё, что угодно. Тут нам на помощь приходит мощь ObjC. Мы можем написать [0x7fafffa54a5 setValue:[UIColor redColor] forKey:@"switchedOffColor"] — и мы уже поменяли значение цвета выключенной лампы на красный, используя стандартные методы NSObject, доступные нам из коробки. Но что, если нам недостаточно этих методов, а нужно «дёрнуть» за свои рычаги? Всё просто — мы можем использовать кастинг: [(MyLamp *)0x7fafffa54a5 powerOff]. Используя подобные техники можно воздействовать на любые сервисы, менеджеры и вью модели вашего приложения в любой момент времени. Мы можем сохранить значение этого адреса в переменную для удобства: (MyLamp *)$lamp = 0x7fafffa54a5. Важно, что название переменной должно начинаться со знака доллара. Это переменная будет жить до полной остановки программы, то есть ей можно пользоваться не только в текущем сеансе отладки, но и при следующем прерывании программы в рамках одного запуска. ObjectiveС предоставляет поистине широкие возможности для того, чтобы похакать текущее состояние и обойти многие ограничения, но что делать с классами, доступными только в Swift? Конечно же, при попытке кастинга Swift-класса в ObjC-контексте ничего не произойдёт. К счастью, в Swift есть подобный механизм. Точнее, функция, имя которой — unsafeBitCast. Мы вправе использовать его с адресом: unsafeBitCast(0x7fafffa54a5, to: MySwiftLamp.self) и получить экземпляр класса MySwiftLamp по адресу. Помните, её использование небезопасно, о чём нам намекает её имя, и её крайне осторожно нужно применять в коде приложения. Хотя, когда вам осознанно нужно будет использовать эту функцию, вы будете достаточно опытны для таких предупреждений.</p><h3>View Hierarchy</h3><figure><img src="https://media.tproger.ru/uploads/2020/04/Panel-instrumentov-otladchika-Xcode-s-podsvechennoj-knopkoj-View-Hiererchy.png" alt="" /></figure><p>Рядом со инструментом Debug Memory Graph соседствует другой, не менее полезный инструмент, — View Hierarchy. Он позволяет быстро найти нужную View, посмотреть её параметры и лейаут, посмотреть активные и неактивные констрейнты. С iOS 11 этот инструмент ещё научился отображать ViewController’ы в иерархии, таким образом находить нужную View стало легче. Неочевидным тут является возможность фильтрации по имени и возможность отключить/включить отображение View, скрытых за экраном. Также я обратил внимание, что редко кто пользуется панелью управления внизу окна визуального отображения View.</p><figure><img src="https://media.tproger.ru/uploads/2020/04/Panel-instrumentov-otladchika-View-Hierarchy.png" alt="" /></figure><p>Кроме того, что она может регулировать глубину просмотра иерархии, она позволяет указать «включить отображение обрезанного контента» и «включать отображение констрейнтов». Обязательно поиграйтесь со всеми инструментами, я уверен — вы найдете полезное для себя применение для некоторых из них. Но в рамках этого рассказа нам нужна только возможность найти нужную View и узнать её адрес. Далее действуем по накатанной: po unsafeBitCast(0x7fafffa54a5, to: UIView.self), но в таком случае мы получим ошибку. Мы сейчас находимся в контексте ObjectiveC и не можем использовать po со Swift-кодом. Мы вынуждены использовать команду expession, или просто e с указанием языка: e -l Swift -- unsafeBitCast(0x7fafffa54a5, to: UIView.self), но и тут наши попытки не увенчаются успехом, мы получим ошибку error: &lt;EXPR&gt;:3:35: error: use of unresolved identifier 'UIView'. Это произойдет из-за модульной природы Swift’а. Для успешного выполнения операции нам потребуется сделать импорт модуля UiKit: e -l Swift -- import UIKit, и после этого мы наконец добьёмся результата: e -l Swift -- unsafeBitCast(0x7fafffa54a5, to: UIView.self).</p><p>Ура! Мы получили описание в консоли. Теперь давайте попробуем поменять, скажем, цвет её бэкграунда. Для начала сохраним View в переменную, чтобы облегчить процесс доступа к ней. Как и в случае с ObjectiveC, при создании переменной в LLDB контексте её название должно начинаться со знака доллара: e -l Swift -- let $view = unsafeBitCast(0x7fafffa54a5, to: UIView.self), далее мы можем применить необходимые изменения: e -l Swift -- $view.backgroundColor = .red. Чтобы увидеть изменения, необходимо продолжить выполнение программы. Но есть способ увидеть изменения и без этого, находясь в режиме «паузы». Дело в том, что мы не видим изменения не потому, что приложение приостановлено, а потому, что все изменения UIView копятся в транзакцию CALayer и применяются только в конце «вращения» текущего RunLoop’а с помощью вызова CATrasaction.flush(). Когда приложение приостановлено для отладки, операционная система всё ещё живёт своей жизнью, вы можете свернуть это приложение и открыть другое. Операционная система всё ещё опрашивает состояние UI вашего приложения и отрисовывает ваше приложение несколько десятков раз в секунду, только RunLoop приостановлен, CATrasaction.flush не вызывается, изменения не применяются. Да, достаточно сделать вызов e -l Swift -- CATrasaction.flush(), и мы увидим изменения.</p><p>На этом пора завязывать. Надеюсь, приведённые примеры кому-то облегчат жизнь, сохранят время и нервы. Добавьте в закладки, и в следующий раз, когда на поиск и отладку очередного бага у вас будет уходить более 15 минут, загляните в эту статью — возможно, какой-нибудь приём вам пригодится.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что труднее всего даётся разработчику и что с этим делать: 5 практических советов</title>
      <link>https://tproger.ru/blogs/what-is-difficult-for-a-programmer-and-what-to-do-with-that</link>
      <comments>https://tproger.ru/blogs/what-is-difficult-for-a-programmer-and-what-to-do-with-that?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/blogs/what-is-difficult-for-a-programmer-and-what-to-do-with-that</guid>
      <description><![CDATA[<p>Начинающие разработчики часто не учитывают назначение программы. Практические советы: проверять вводимые данные и думать о пользователе.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/blogs/what-is-difficult-for-a-programmer-and-what-to-do-with-that">Что труднее всего даётся разработчику и что с этим делать: 5 практических советов</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Блоги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 16 Sep 2019 13:11:34 GMT</pubDate>
      <content:encoded><![CDATA[<p>Владимир Масальцев, руководитель группы разработки ПО Тверского Технологического центра Accenture Russia</p><p>Начинающий разработчик часто не задумывается, для чего предназначена программа и как она будет использоваться в будущем. У многих на первый план выходит соответствие стандартам качества кода, а не то, как этот код будет использоваться в реальной жизни.</p><p>Если постановщик задачи недостаточно опытный или у него нет времени вникать в бизнес-процессы, может возникнуть ситуация, когда в тестовом режиме программа работает корректно, а при переходе в продуктивную версию демонстрирует кучу ошибок и недочётов. Разберёмся, что нужно учитывать начинающему программисту, чтобы разработка оказалась полезной для конечного пользователя.</p><h2>Шаг 1: Доверяй, но проверяй</h2><p>Начинающие разработчики часто реализуют «хорошие» алгоритмы, которые прекрасно работают с «правильными» данными. Они не обращают внимание на поведение программы, когда в качестве исходной информации поступают «неправильные» данные. А именно это, как правило, первым делом и случается, как только программа начинает работать в продуктивной среде.</p><p>Учесть абсолютно все возможные ситуации программисту, конечно, сложно, но работать в этом направлении необходимо. Алгоритм должен иметь проверки в каждом блоке кода и генерировать для пользователя сообщения с указанием предупреждений или ошибок в данных.</p><h2>Шаг 2: Встаньте на место пользователя</h2><p>Ещё одна сложность может быть связана с пользователями программы. Любой пользователь, даже имея чёткую инструкцию по работе с программой, всегда может нажать такую комбинацию кнопок или ввести такие данные, которые сломают «хороший» алгоритм, если он не имеет защиты.</p><p>Для этих целей на крупных проектах часто есть тестировщики или программные средства автоматического тестирования. Возникает вопрос, что делать, если этого нет.</p><p>Разработчику представить себя на месте пользователя на самом деле не так трудно, как может показаться. Пользователи тоже люди и способны совершать ошибки. Нужно не полениться и нажать на все подряд средства навигации, а также ввести некорректные данные во все возможные поля.</p><h2>Шаг 3: Комментируйте логику кода при его написании</h2><p>Сложно приучить себя писать код программы и сразу же объяснять его логику в комментариях. Здесь необходимо пояснение: не нужно расшифровывать каждую строку и очевидные вещи вроде инициализации значений переменных, но назначение небольших блоков кода можно легко описать одной строкой текста.</p><p>В большинстве случаев достаточно будет одного пояснения на каждые 10–20 строк кода, это сильно упростит последующую поддержку и модификацию приложения.</p><h2>Шаг 4: Ничего не откладывайте на потом</h2><p>Разработчики могут сами создавать себе неявные проблемы при работе над многодневной задачей, когда сознательно пропускают неосновные блоки в коде. Например, расширенные проверки данных перед их обработкой или логирование там, где это необязательно. Это впоследствии может помешать разобраться с возникающими ошибками в программе.</p><p>Такие вещи выглядят не слишком существенными на фоне других задач. Создаётся ощущение, что их можно пропустить и вернуться к ним позже. Самое трудное — осознать, что потом может не наступить. После тестирования и исправления дефектов на доработки часто просто не остаётся времени. Не стоит пропускать или откладывать что-либо в реализации алгоритма, особенно если работа над ним занимает больше одного дня.</p><h2>Шаг 5: Сохраняйте спокойствие и выдержку</h2><p>Нередко мы попадаем в ситуации, когда времени в обрез: авралы бывают у всех. Спешка в разработке приводит к тому, что программа на выходе способна запускаться и выполнять некоторые действия, но имеет серьёзные провалы в структуре.</p><p>В лучшем случае код может содержать кучу «мусора», оставшегося от попыток решения алгоритмических проблем. А в худшем — программа может просто не быть в достаточной степени отлажена и протестирована, чтобы справляться со всем спектром возложенных на неё бизнес-процессов.</p><p>Это обязательно приведёт к необходимости часто дорабатывать программу из-за постоянно возникающих инцидентов в продуктивной среде. Необходима трезвая оценка своих возможностей и спокойная реализация без паники и страха что-то не успеть.</p>]]></content:encoded>
    </item>
    <item>
      <title>Работа в реальном проекте: советы начинающим программистам</title>
      <link>https://tproger.ru/blogs/real-project-advices</link>
      <comments>https://tproger.ru/blogs/real-project-advices?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Никита Прияцелюк]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/blogs/real-project-advices</guid>
      <description><![CDATA[<p>Начинающие программисты на первом реальном проекте совершают одни и те же ошибки. Важные моменты в работе — от Senior TeamLeader в компании DataArt.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/blogs/real-project-advices">Работа в реальном проекте: советы начинающим программистам</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Блоги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 09 Jun 2019 07:41:18 GMT</pubDate>
      <content:encoded><![CDATA[<p>Рассказывает Олег Меринов, Senior Development TeamLeader в DataArt</p><p>По работе я много общаюсь с интернами и джуниорами — вчерашними студентами, которые приходят в проектные команды из университетов. Устраиваются, конечно, инженерами, а опыта у них нет. На протяжении многих лет я замечал, что проблемы, с которыми они сталкиваются на первых порах, одни и те же.</p><p>Я решил рассказать о некоторых важных моментах, которые приходится обсуждать едва ли не с каждым, кто пока не занимался никакими проектами, кроме учебных. Я специализируюсь на .NET, но эти советы вполне применимы к любому языку, хоть к Java, хоть к Python. Ведь все мы должны сделать одно и то же: написать, протестировать, зарелизить и поддерживать.</p><h2>Оценка требований</h2><p>Вспоминаю себя студентом: едва получив задачу, я садился кодить. Думаю, это нормально, а уж для студента просто естественно — кидаться в бой, как только вручили условие.</p><p>Но лучше вначале задуматься о том, чего от вас хотят — неважно, лабораторная это или ваша часть коммерческого проекта. Важно внимательно прочитать условия задачи и выслушать комментарии руководителя или старшего коллеги.</p><p>Многие ребята, в том числе талантливые, хорошо справляющиеся с любыми учебными заданиями, быстро схватывают суть и думают, что отлично поняли, что нужно сделать. Они двигаются дальше, концентрируются на деталях, а когда приходят спустя два дня (хорошо) или неделю (хуже), выясняется, что созданный ими код не работает или работает не так, как требуется.</p><p>Поэтому не стесняйтесь задавать вопросы. К сожалению, в наших школах и университетах люди привыкают: если ты что-то спрашиваешь, все думают, что ты ничего не понимаешь. Но на работе уточнить все, даже казалось бы, очевидные, детали критически важно. Никто над этим смеяться не будет, коллеги всё вам охотно расскажут.</p><p>Когда вы уяснили задачу и даже нашли примерное решение — поделитесь им, расскажите алгоритмы, которые пришли вам в голову. Реакция старшего коллеги сразу подтвердит или опровергнет вашу догадку, даст почву для дальнейшего исследования. Вообще делитесь своими идеями. Это тоже противоречит школьному стереотипу: отличникам категорически нельзя давать списывать. И многие молодые инженеры по привычке боятся рассказать, что они придумали. Но только получив отзыв на свой вариант решения, вы расширяете картину мира — возможно, это позволит посмотреть на задачу с другой, как раз нужной вам, стороны.</p><p>Узнайте об ограничениях — в реальном проекте это может оказаться чуть ли не самым главным. Спросите, сколько данных будет поступать на вход приложения, какая планируется нагрузка. Такая информация поможет и при проектировании решения, и при его сдаче.</p><p>Ещё один совет: делайте блок-схемы. Признайтесь, вы ведь их не рисуете? Я знаю, многие уверены, вся эта сортировка «пузырьком» — просто глупости. На самом деле, в будущем это поможет вам, поскольку схематичное описание решения позволит очень быстро донести до коллег вашу идею.</p><p>Что проще: изобразить схему с тремя блоками и связями между ними или исписать половину листа А4? К тому же, картинку проще воспринимать, а читать длинные тексты никому не хочется. Обратите внимание на UML — язык, на котором можно писать и физические модели базы данных, и концептуальные модели, и диаграммы последовательности вызовов, и многое другое.</p><h2>Оценка сложности</h2><p>Об оценке сложности часто забывают, а вспоминают, только когда при выходе в продакшен сваливаются огромные объёмы данных и система проседает по производительности.</p><p>Основываясь на блок-схемах, диаграммах последовательности вызовов и компонентов, существующих ограничениях вы можете оценить сложность задачи.</p><h2>Разработка</h2><p>Главный вопрос: как писать? Один проджект-менеджер выразился довольно точно: «надо писать без багов всегда». И здесь самое главное: naming convention и code style.</p><p>Naming convention (соглашение об именах) необходимо, чтобы код было удобно читать. Это может быть не совсем понятно в случае с лабораторной или курсовой, но обычно работать приходится в команде. И кодом владеют все разработчики, а не только тот, кто сейчас пилит отдельный компонент системы. А соглашение об именах позволяет легко понять, что написал твой коллега.</p><p>Той же цели служит и Code style (стандарт оформления кода) — стиль оформления кода, необходимый для унификации. Программный код, разработанный разными людьми, должен читаться так, как будто его написал один программист. Поэтому, придя в проект, в первую очередь, нужно понять, какие naming convention и code style здесь используют. Помните, что строгое следование им — ещё и знак уважения к коллегам, который они обязательно оценят.</p><figure><img src="https://media.tproger.ru/uploads/2019/06/image5.png" alt="" /></figure><p>Var — хорошая практика, которая позволяет любому разработчику читать код с листа. Тем не менее многие компании делают для проектов собственные конвенции.</p><p>Но для запускающегося проекта проще всего взять готовые конвенции. Сейчас многие сообщества и компании их публикуют, есть даже целые библиотеки, где можно проверить код на ошибки стиля.</p><p>В своих примерах Microsoft продаёт велосипеды, а я буду считать налоги.</p><figure><img src="https://media.tproger.ru/uploads/2019/06/image7.jpg" alt="" /></figure><p>Пишите простой код! Элементарный совет, следовать которому очень сложно. Если инженер не до конца продумал решение задачи, не смог сделать декомпозицию (не разбил задачу на составные части) — в методе мы видим 500 строк кода. Это не так страшно, но важно вернуться назад и подумать над декомпозицией ещё раз.</p><p>Почему код должен быть простым? Сейчас код есть только у вас и, возможно, преподавателя. Потом им будет владеть большая команда, которой нужно разбираться в его нюансах с максимально возможной скоростью.</p><p>Чтобы код стал проще, нужно ввести слабую связанность компонентов.</p><p>Представьте, вы написали сложный код, вернулись, разбили на составные части.</p><p>В примере с налогами у меня есть некий класс, который считает сумму налога. Я разбил на класс, который представляет работника, и на класс, который в зависимости от адреса или другой логики возвращает процент налога — это пример слабой связанности.</p><p>Но можно ввести дополнительные элементы, например, интерфейс для класса taxEvaluator для employee.</p><p>Что такое IoC?</p><figure><img src="https://media.tproger.ru/uploads/2019/06/image3.jpg" alt="" /></figure><p>Этот паттерн называется Inversion of control. В микросервисах слабая связанность приводит к масштабированию системы. Когда у нас есть интерфейс, мы можем изолировать наш основной класс с логикой и протестировать его.</p><p>Ещё здесь не хватает комментариев. Комментарий — это важный и полезный элемент работы. Когда вы пишете сложный алгоритм, то комментарии помогут понять, что там вообще происходит, особенно они важны для регулярных выражений. Я пишу регулярное выражение, а через 15 минут забываю, что оно делает.</p><p>Проверяйте аргументы — это частая проблема молодых инженеров: они уверены, что всё написали чётко и в проверках написанное ими не нуждается. Но это не так!</p><figure><img src="https://media.tproger.ru/uploads/2019/06/image1.png" alt="" /></figure><p>В этом примере мы выбрасываем исключение, но не всегда должны это делать.</p><p>Вообще исключение — это особая тема. Чтобы понять, что с ним делать, можно почитать «Программист — прагматик» Дэйва Томаса и Энди Ханта. Книга старая, но очень интересная, там хорошие и здравые идеи, когда выбрасывать, когда нет, когда считать, что это ошибка бизнес-, а не программного уровня. Но у нас tax evaluator = null — это ключевая функциональность и без неё мы работать не можем, поэтому выбрасываем исключение.</p><p>Когда к нам приходит пустой объект с работником — employee — то мы вновь ничего не можем сделать, так как на этом завязана вся логика.</p><p>Исключения надо обрабатывать</p><figure><img src="https://media.tproger.ru/uploads/2019/06/image2.jpg" alt="" /></figure><p>«Try catch» необходимо использовать либо с типизированными исключениями, либо когда мы не знаем, какое исключение прилетит. Надо залогировать, но ни в коем случае не возвращать такие конструкции.</p><p>Мы «глотаем» исключения, и поэтому никогда не увидим их в логах. И как будто всё хорошо и работает, но если будет проблема, мы не сможем её поймать. Исключения нужны, но используйте их очень аккуратно.</p><p>Можно убрать «try catch» и обработать исключение как бизнес-кейс и логировать. Записывайте в логи ход выполнения ваших алгоритмов и программ, потому что во время разработки ваш софт доступен через дебаггер и вы всегда можете посмотреть, что происходит. Но когда исключение уходит туда, куда у вас нет доступа, то только логи могут вам помочь.</p><h2>Unit test</h2><p>Разработчик должен писать юнит-тесты. Понятно, почему этим не занимаются, но писать их стоит. Посмотрите на методики написания юнит-тестов — когда вы будете делать собственные, то будете иначе проектировать ваши решения, программы и задачи.</p><p>Потому что как раз разбиение и декомпозиция — это первый шаг к тому, чтобы начать писать юнит-тесты, но и сами юнит-тесты вас будут двигать к декомпозиции.</p><h2>Сборка, деплой, конфигурация</h2><figure><img src="https://media.tproger.ru/uploads/2019/06/image6.jpg" alt="" /></figure><p>Что мы делаем, после того как написали что-то? Мы делаем сборку, потом собранные элементы где-то храним, затем деплоим.</p><p>Это цикл жизни от момента создания до деплоя, и этот код будет использовать конечный пользователь. Я написал, какие инструменты могут быть использованы: создать автоматическую сборку можно с помощью PowerShell и msbuilt; хранить — в ProGet, и дальше мы можем деплоить полученные пакеты. Это немного сложно.</p><h2>Работа над ошибками, изменение требований</h2><p>Мы всё поняли, всё запрограммировали, протестировали, отдали, а заказчик говорит: всё не то.</p><p>Что делать? Возвращаться к самому началу и спрашивать: что не то? Если изменились требования — уточнить их и прорабатывать дальше.</p><h2>Что почитать?</h2><ul><li>Брайан У. Керниган, Деннис М. Ритчи «The C Programming Language» — достаточно старая. Она произвела на меня большое впечатление. Во многом не про язык программирования, а про верный майнд-сет.</li><li>МакКоннел «Code Complete»— маст рид.</li><li>Дэйв Томас, Энди Хант «The Pragmatic Programmer».</li><li>Альфред Ахо «Data Structures and Algorithms» — занудная, всегда, когда читаю, засыпаю. Да, она читается сложно, но она очень классная.</li><li>Джон Влисидис, Ральф Джонсон, Ричард Хелм, Эрих Гамма «Design Patterns» — книга для тех, кто планирует писать на объектно-ориентированных языках программирования. Она тоже задаёт правильный майнд-сет. Когда я читал, то ничего не понял, но когда начал работать, перечитал, и книга начала помогать.</li><li>Мартин Фаулер «Catalog of Patterns of Enterprise Application Architecture» — про архитектуру приложений. Фаулер внёс огромный вклад в индустрию, написал разные паттерны, на основе которых создано много софта, те же ORM (object relation mapping).</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Неофициальный и консервативный FAQ по Django</title>
      <link>https://tproger.ru/translations/django-faq</link>
      <comments>https://tproger.ru/translations/django-faq?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Сергей Ринг]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/django-faq</guid>
      <description><![CDATA[<p>Django или Flask, чем хорош этот веб-фреймворк и что выбирать в спорных ситуациях — ответы на вопросы, которых нет в официальной документации.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/django-faq">Неофициальный и консервативный FAQ по Django</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Django]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 20 May 2019 08:10:02 GMT</pubDate>
      <content:encoded><![CDATA[<p><a href="https://www.djangoproject.com/start/overview/">Django</a> быстр, многофункционален, безопасен, масштабируем, гибок. Он хорошо взаимодействует с различными рабочими потоками, инструментами, платформами и библиотеками.В этом FAQ вы найдёте ответы на часто возникающие вопросы, которых нет в официальном FAQ. Конечно, универсальных ответов нет, но, возможно, этот FAQ сможет немного прояснить обстановку.</p><h2>Flask или Django?</h2><p>В большинстве случаев — Django.</p><p>Но, если у вас маленькое приложение или скрипт, и вы просто хотите добавить HTTP API, то, конечно же, используйте Flask.<br />Но в других случаях, с усложнением ваших приложений, вам нужно будет внедрять всё больше и больше функциональности, вы будете использовать всё больше и больше библиотек, и с Flask’ом вы просто запутаетесь. Поэтому для больших проектов используйте Django.</p><h2>Нужно ли разбивать свой код на отдельные приложения?</h2><p>Скорее нет.</p><p>Разделение полезно при многократном использовании повторяющихся функций. Когда вы делаете своё собственное приложение, а особенно, если вы только новичок, организовать всё в одном месте будет намного лучше. В какой-то момент вам, возможно, захочется разделить всё на отдельные модули, и в этот момент у вас уже будут намечены чёткие границы, где и что разделять. Это лучше, чем сначала создать структуру, а потом бороться с последствиями изменений.</p><h2>Стоит использовать DTL (Django’s default template language) или что-нибудь другое?</h2><p>Используйте DTL.</p><p>По умолчанию он уже есть в пакете Django, и в принципе может выполнять всё, что вам нужно.</p><h2>DTL не делает то, что мне нужно, могу я использовать Jinja2?</h2><p>Всё равно используйте DTL.</p><p>Наиболее частая претензия: “DTL недостаточно гибкий”. Но нужно учитывать, что есть определённые алгоритмы, реализовать которые на шаблонном языке невозможно.</p><p>Запомните, все “мозги” вы содержите во Views, а шаблоны выступают в роли визуализаторов.</p><h2>Что использовать для запуска Django — uWSGI или Gunicorn?</h2><p>Однозначно <a href="https://gunicorn.org/">Gunicorn</a>.</p><p>Gunicorn — автономный WSGI HTTP сервер, довольно неплохой, развёртывание происходит быстро и просто.</p><p>uWSGI предоставляет полный стек для построения хостинг-сервисов, если это то, что вам нужно — используйте его.</p><h2>Где мне стоит разворачивать своё приложение?</h2><p>В качестве примера посмотрите, <a href="https://tproger.ru/translations/telegram-bot-create-and-deploy/">как написать Telegram-бот на Python и задеплоить его на Heroku</a>.</p><p>Если вам нужно больше управляемости или вы привязаны к AWS, то используйте AWS Elastic Beanstalk.</p><p>Вы, конечно, можете взять простенькую VM, установить туда nginx и настроить всё сами. Но зачем, если для этого уже существуют готовые решения?</p><p>Создать свою собственную платформу то же самое, что написать новую ОС. Зачем возиться?</p><h2>Что использовать для обслуживания статических файлов?</h2><p><a href="http://whitenoise.evans.io/en/stable/">Whitenoise</a>.</p><p>Amazon S3, Nginx, Apache — все они хороши, но Whitenoise относительно маленькая библиотека, которая помогает обслуживать статические файлы без использования сторонних сервисов.</p><h2>Я пытаюсь заставить панель администратора сделать …</h2><p>Может быть, не надо? Панель администратора Django — хорошая штука, но ссылаясь на официальную документацию:</p><blockquote>Возможности панели ограничены. Она не предназначена для того, чтобы в неё ещё что-нибудь добавляли.<br />У панели администратора есть много возможностей для кастомизации, но не переусердствуйте. Если вам нужен более процессо-ориентированный интерфейс, который абстрагирует базы данных, поля и таблицы, время задуматься о написании своей панели.</blockquote><p>Если вы до сих пор ломаете голову, как подстроить администратора под себя, — перестаньте. Пойдите и попробуйте сделать что-нибудь с <a href="https://docs.djangoproject.com/en/2.1/ref/class-based-views/">generic views</a>.</p><h2>Какую базу данных использовать?</h2><p><a href="https://www.postgresql.org/">PostgreSQL</a>.</p><p>Она просто работает.</p><h2>Стоит ли разделять файл конфигурации между разработкой и продакшном?</h2><p>Нет, не стоит.</p><p><a href="https://12factor.net/config">Twelve-Factor App</a> охватывает ряд причин, по которым файлы конфигурации в приложении для каждой среды могут быть проблематичными, и почему вместо этого стоит использовать переменные окружения. <a href="https://github.com/doismellburning/django12factor/">django12factor</a> и другие приложения помогут вам получить конфигурацию из окружения.</p><h2>Стоит ли использовать кастомную модель пользователя?</h2><p>Из официальной документации:</p><blockquote>При запуске нового проекта настоятельно рекомендуется настроить модель пользователя, даже если вам достаточно установок по умолчанию. Эта модель будет вести себя идентично модели по умолчанию, но вы сможете модифицировать её в будущем, если возникнет необходимость.</blockquote><p>Если вы находитесь посреди процесса разработки, всё немного сложнее…</p><blockquote>Это изменение не может быть сделано автоматически и требует ручной работы, перемещения данных из старой таблицы пользователя и, возможно, ручного повторного применения некоторых миграций.</blockquote>]]></content:encoded>
    </item>
    <item>
      <title>Ускоряем загрузку своего сайта</title>
      <link>https://tproger.ru/translations/how-to-boost-frontend</link>
      <comments>https://tproger.ru/translations/how-to-boost-frontend?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Александр Ланский]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/how-to-boost-frontend</guid>
      <description><![CDATA[<p>Способы ускорить интерфейсные приложения на Angular, React и VueJS: сбор показателей производительности через Lighthouse и работа над конверсией.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/how-to-boost-frontend">Ускоряем загрузку своего сайта</a>»</p>]]></description>
      <category><![CDATA[Веб-разработка]]></category>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 14 May 2019 07:43:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>Оптимизация загрузки положительно влияет на отношение клиентов к продукту, уровень конверсии, SEO, и, в итоге, на успех вашего проекта.</p><blockquote>Если сайт загружается дольше трёх секунд, 53 % пользователей покинут его.Google</blockquote><p>Предположим, у вас уже есть готовый сайт или приложение, которое использует современный JS-фреймворк (Angular, React, VueJS). Как убедиться, что текущая производительность не мешает вашему продукту стать успешным?</p><p>Вначале стоит собрать данные о производительности фронтенда вашего приложения. Есть два отличных инструмента:</p><ul><li><a href="https://developers.google.com/web/tools/lighthouse/">Lighthouse</a> от Google Сhrome;</li><li><a href="https://speedcurve.com/">Speedcurve</a>.</li></ul><p>Оба эти инструмента позволяют отслеживать основные показатели производительности вашего приложения (KPI).</p><p>Lighthouse уже входит в Chrome DevTools. Проанализировав ваш сайт/веб-приложение, этот инструмент может дать дельные советы по оптимизации.</p><figure><img src="https://media.tproger.ru/uploads/2019/05/lighthouse-hints.jpg" alt="" /></figure><p><a href="https://speedcurve.com/">Speedcurve</a> делает всё то же самое, но данные можно просматривать ещё и в динамике.</p><p>Теперь, когда есть возможность измерять производительность, приступим к оптимизации сайта.</p><h2>Изображения</h2><p>Изображения занимают достаточно большую часть загружаемых на сайт данных, поэтому их оптимизация крайне важна для производительности сайта.</p><h3>Адаптация размеров изображений</h3><p>Первым делом нужно убедиться, что размеры изображений не больше, чем это необходимо. Поэтому нужно подогнать их в точности под размеры на макете.</p><figure><img src="https://media.tproger.ru/uploads/2019/05/response.jpg" alt="" /></figure><p>К тому же нужно убедиться, что сами изображения так же адаптивны, как и макет. <a href="https://www.responsivebreakpoints.com/">Responsive Image Breakpoints Generator</a> выдаёт все необходимые версии изображений и HTML-код для их вставки. Достаточно будет 8–10 сгенерированных изображений.</p><p>Сгенерированный в этом инструменте HTML-код можно будет использовать не только на сайтах, но и в веб-приложениях. Для тех, кто работает с Gulp, есть специальный плагин <a href="https://github.com/mahnunchik/gulp-responsive">gulp-responsive</a>, который автоматизирует этот процесс.</p><h3>Не забываем про ленивую загрузку</h3><figure><img src="https://media.tproger.ru/uploads/2019/05/lazy-load.jpg" alt="" /></figure><p>Ленивая загрузка (англ. lazy load) предполагает, что не все изображения будут загружены сразу при открытии страницы — часть будет подгружаться после, при необходимости. Изображение, которое не находится в текущей области видимости, будет подгружаться в будущем (главное помнить, что изображение нужно подгружать чуть раньше, чем оно должно появиться в области видимости).</p><p>Независимо от того, какой фреймворк вы используете, в нём, вероятнее всего, будет плагин для ленивой загрузки изображений (к примеру, <a href="https://github.com/alexjoverm/v-lazy-image">v-lazy-image</a> в Vue). Хотя ничто не мешает вам самим реализовать эту функциональность. Чтобы проверить, находится ли элемент в области видимости, хорошо подходит <a href="https://developer.mozilla.org/ru/docs/Web/API/Intersection_Observer_API">IntersectionObserver API</a>.</p><h3>Доставляем изображения через CDN</h3><p>Если вы уже оптимизировали размер изображений и их количество и ждёте посетителей со всего мира, то вам не помешает CDN — Content Delivery Network (Сеть доставки содержимого) для обслуживания изображений.</p><p>Эта система кеширует изображения на глобально-распределённой сети серверов. Допустим, если кто-нибудь из Австралии запросит изображение с вашего веб-сайта, то вместо того, чтобы загружать это изображение с сервера в Европе, CDN загрузит его с ближайшего к Австралии сервера. Такая система ускоряет процесс загрузки изображений на сайте.</p><h2>CSS, JS и HTML</h2><p>Все современные фреймворки в процессе сборки оптимизируют ваш код (code-splittiong, tree shaking, minification и др.). Что ещё можно придумать?</p><h3>Оптимизация основной HTML-страницы</h3><p>HTML-документ — это скелет почти всех веб-приложений. При работе с ссылками на внешние ресурсы стоит придерживаться нескольких советов:</p><ul><li>Помещайте ссылки на CSS вверху HTML-документа для ускорения загрузки страницы.<br />Прим. перев. Есть более оптимальные способы ускорения загрузки, например вынесение критичного css в тег style, а для оставшихся стилей (обязательно соберите их в один файл с помощью какого-либо сборщика фронтенда) применять <a href="https://developer.mozilla.org/ru/docs/Web/HTML/Preloading_content">предварительную загрузку</a> или отправлять их с помощью http/2 Server Push;</li><li>Размещайте JS-скрипты внизу страницы. Это предотвращает блокировку рендеринга страницы тегами &lt;script&gt;. И не забывайте про <a href="https://www.w3schools.com/tags/att_script_async.asp">асинхронные скрипты</a>.<br />Прим. перев. Правильнее добавить скрипты в начале и объявить их с <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script">атрибутами async или defer</a>. Используйте defer, если для выполнения скрипта нужно дождаться завершения загрузки страницы, async — если этого не требуется;</li></ul><h3>Убедитесь, что загружается только необходимое</h3><p>В таких фреймворках, как Angular, React и VueJS, есть поддержка ленивой загрузки ресурсов. Будет достаточно грамотно разделить код и загрузить только те модули, которые нужны. К примеру, если у вас коммерческое приложение, то на главной странице не нужно подгружать модули Basket или Payments.</p><h2>Сжатие и кеширование</h2><p>Ко всем важным ресурсам фронтенда (изображения, код) необходимо применять сжатие и грамотно кешировать их.</p><p>Сжатие файла делает его немного легче и ускоряет загрузку. Один из самых популярных методов сжатия — это <a href="https://ru.wikipedia.org/wiki/Gzip">gzip</a>. Он прекрасно подходит для сжатия кода, документов, изображений и аудио-файлов.</p><p>Кроме gzip, существует ещё и <a href="https://github.com/google/brotli">Brotli,</a> популярность которого постепенно возрастает. Это алгоритм с открытым исходным кодом, который постоянно обновляется программистами из Google и других организаций. Этот метод сжатия зарекомендовал себя намного лучше остальных.</p><p>Вы можете активировать предпочтительный метод сжатия на Nginx, Apache или любом другом сервере, изменив его файл конфигурации (активация brotli на nginx, <a href="https://ayesh.me/apache-brotli">на apache</a>).</p><p>Когда дело доходит до кеширования — чаще всего используют метод под названием Leverage Browser Caching. Вы можете <a href="https://www.keycdn.com/blog/leverage-browser-caching">активировать его в файле конфигурации вашего сервера</a>.</p><h2>Заключение</h2><p>Если речь идёт о сайтах или интерфейсных приложениях, то их производительность практически всегда является критическим фактором. Для более тщательной проверки оптимизации сайта/приложения советуем пройтись по <a href="https://github.com/thedaviddias/Front-End-Checklist">Front-End-Performance-Checklist</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Меняем схему базы данных в PostrgreSQL, не останавливая работу приложения</title>
      <link>https://tproger.ru/translations/postgres-ddl-without-downtime</link>
      <comments>https://tproger.ru/translations/postgres-ddl-without-downtime?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Пётр Соковых]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/postgres-ddl-without-downtime</guid>
      <description><![CDATA[<p>Опыт Braintree Payments: как обновлять схему PostgreSQL, когда простой API недопустим даже на минуты, и какие требования предъявляются к DDL-операциям.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/postgres-ddl-without-downtime">Меняем схему базы данных в PostrgreSQL, не останавливая работу приложения</a>»</p>]]></description>
      <category><![CDATA[Лучшая практика]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 07 May 2019 07:51:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Эта статья описывает опыт Braintree Payments, подразделения PayPal, и рассказывает о том, как им удаётся обновлять схему баз данных PostgreSQL в условиях, когда приостановка работы API для подобных технических работ недопустима — даже если речь идёт о минутах.</p><p>В этой статье будут рассмотрены следующие темы:</p><ul><li><a href="https://tproger.ru/#tddl">Транзакционный DDL (Data Definition Language)</a>;</li><li><a href="https://tproger.ru/#lock">Блокирование строк</a>;</li><li><a href="https://tproger.ru/#optable">Операции над таблицей</a>;</li><li><a href="https://tproger.ru/#opcolumn">Операции над столбцами</a>;</li><li><a href="https://tproger.ru/#opindex">Операции над индексами</a>;</li><li><a href="https://tproger.ru/#constraints">Ограничения</a>;</li><li><a href="https://tproger.ru/#enums">Перечисляемые типы</a>;</li><li><a href="https://tproger.ru/#libror">Бонус: библиотека для Ruby on Rails</a>.</li></ul><h2>Немного основ</h2><p>Ко всему коду и ко всем изменениям баз данных в Braintree выдвигаются следующие требования:</p><ul><li>Актуальный код и схемы БД должны иметь прямую совместимость с новым кодом и новыми схемами. Это позволяет вносить изменения постепенно, а не сразу на всех используемых серверах.</li><li>Новый код и новые схемы должны быть обратно совместимы с актуальным кодом и схемами. Это позволяет без особых проблем отменить любые изменения, в случае возникновения непредвиденных ошибок.</li></ul><p>Для всех DDL операций важно, чтобы:</p><ul><li>Любые блокировки таблиц или индексов держались не более двух секунд.</li><li>Способы отмены вносимых изменений не требовали откатывать схему базы данных к предыдущей версии.</li></ul><h2>Транзакционный DDL</h2><p>PostgreSQL поддерживает транзакции при выполнении DDL операций. В большинстве случаев вы можете выполнять несколько DDL запросов внутри одной транзакции и придерживаться стратегии «всё или ничего». К сожалению, у такого подхода есть существенный недостаток: если вы меняете несколько объектов, вам придётся заблокировать их все. Блокировка нескольких таблиц, во-первых, создаёт вероятность взаимной блокировки (deadlock), а, во-вторых, вынуждает пользователей ждать выполнения всей транзакции. Поэтому для каждого запроса рекомендуется использовать отдельную транзакцию.</p><p>Заметьте: параллельное создание индексов — это особый случай. PostgreSQL запрещает выполнять CREATE INDEX CONCURRENTLY внутри явно описанной транзакции; вместо этого PostgreSQL самостоятельно создаёт транзакции и управляет ими. Если по каким-то причинам построение индекса прерывается до успешного завершения, то может потребоваться вручную удалить его, прежде чем пробовать ещё раз. Впрочем, такой индекс всё равно никогда не будет использоваться для обслуживания запросов.</p><h2>Блокирование строк</h2><p>У PostgreSQL множество <a href="https://www.postgresql.org/docs/current/explicit-locking.html">разных уровней блокировки</a>. Нас интересуют в основном блокировки уровня таблицы (потому что DDL обычно оперирует на этом уровне):</p><ul><li>ACCESS EXCLUSIVE: запрещено любое использование заблокированной таблицы.</li><li>SHARE ROW EXCLUSIVE: запрещены команды DDL, выполняющиеся параллельно, а также модификация строк (чтение разрешено).</li><li>SHARE UPDATE EXCLUSIVE: запрещены только команды DDL, выполняющиеся параллельно.</li></ul><p>Заметьте: Понятие “команды DDL, выполняющиеся параллельно” в данном контексте включают в себя операции VACUUM и ANALYZE.</p><p>Все операции DDL обязательно блокируют таблицу одним из этих способов. К примеру, если вы выполните:</p><p>ALTER TABLE foos ADD COLUMN bar INTEGER; PostgreSQL попытается получить блокировку уровня ACCESS EXCLUSIVE на всей таблице foos.</p><p>Когда вы применяете блокировку такого уровня, ни один из последующих запросов к таблице не выполняется. Вместо этого они откладываются в очередь до тех пор, пока самый долгий из запущенных вами запросов не закончит выполнение. Выполнение запросов, отложенное на определённый срок, нельзя отличить от отключения сервера для выполнения технических работ. Поэтому такой ситуации лучше избегать.</p><h3>Основные подходы</h3><p>Вместо того чтобы полагаться на PostrgeSQL, осуществляйте явную блокировку самостоятельно. Это позволяет аккуратно контролировать время, на которое запросы откладываются в очередь. Если у вас не получается осуществить блокировку в течение нескольких секунд, рекомендуется добавить небольшую задержку перед следующей попыткой. Таким образом вы позволите отложенным запросам выполниться и не создавать слишком большую нагрузку в будущем. И, наконец, прежде чем пытаться осуществить блокировку, запросите из pg_locks1 список долго выполняющихся запросов. Это позволит избежать постановки в очередь команд, которые, скорее всего, не выполнятся.</p><p>Начиная с PostgreSQL 9.3, вы можете настроить параметр lock_timeout, чтобы контролировать то, насколько долго PostgreSQL будет ожидать получения контроля над таблицей. Если вы вдруг используете версию 9.2 или более раннюю (они, к слову, не поддерживаются, вам стоит обновиться!), вы можете добиться того же результата, используя параметр statement_timeout с явным выражением LOCK&lt;table&gt;.</p><p>Зачастую блокировка уровня ACCESS EXCLUSIVE действительно необходима только на очень короткий период, который требуется PostgreSQL, чтобы обновить его catalog tables (таблицы с метаинформацией). Ниже мы рассмотрим случаи, когда достаточно более слабой блокировки или когда можно применить альтернативные подходы, чтобы избежать длительной приостановки SELECT/INSERT/UPDATE/DELETE.</p><p>Обратите внимание: иногда удержание блокировки уровня ACCESS EXCLUSIVE для чего-то большего, чем обновление каталога (или перезаписи) может быть оправдано. Например, если размер таблицы относительно мал. Рекомендуется проверять конкретные случаи использования на реалистичных размерах данных и оборудовании, чтобы увидеть, является ли  операция достаточно быстрой. Если вы используете хорошее оборудование, и ваша таблица легко помещается в память, то полное сканирование таблицы или перезапись тысяч строк всё ещё могут быть достаточно быстрыми.</p><h2>Операции над таблицей</h2><h3>Создание таблицы</h3><p>В общем случае добавление таблицы — одна немногих операций, о которых не стоит слишком заботиться. Ведь объект, который мы «изменяем», просто технически не может использоваться в этот момент.</p><p>Большинство параметров создания таблицы никак не влияют на другие объекты базы данных. Добавление внешнего ключа при определении таблицы заставит PostgreSQL получить блокировку уровня SHARE ROW EXCLUSIVE на упомянутой таблице. Это остановит все DDL запросы, направленные к ней, и модификации строк. Несмотря на то что эта блокировка не должна быть слишком долгой, о ней стоит помнить, как и о любой другой операции, вызывающей блокировку. Рекомендуется разделять эти две операции: создайте таблицу, и только потом <a href="https://tproger.ru/#foreignkey">добавьте внешний ключ</a>.</p><h3>Удаление таблицы</h3><p>Удаление таблицы по понятным причинам требует блокировки уровня ACCESS EXCLUSIVE. Если таблица уже не используется, вы можете спокойно её удалить. Но прежде чем на самом деле выполнить DROP TABLE ... вам следует проверить документацию и код, чтобы удостовериться, что все упоминания о ней на самом деле стёрты. Чтобы перепроверить это, вы можете запросить у PostgreSQL статистику использования таблицы (используя представление pg_stat_user_tables2).</p><h3>Переименование таблицы</h3><p>Вероятно, ни для кого не станет открытием, что переименование таблицы требует ACCESS EXCLUSIVE. Очень маловероятно, что ваш код сможет безопасно обработать переименование таблицы прямо налету — это возможно только если в таблицу никто не пишет и не берёт из неё данные.</p><p>Рекомендуется избегать переименований таблиц везде, где это возможно. Но если переименование действительно необходимо, то для безопасности:</p><ul><li>Создайте новую таблицу с такой же схемой, как предыдущая.</li><li>Скопируйте данные из старой таблицы в новую.</li><li>Создайте триггеры на INSERT и UPDATE к старой таблице, чтобы при её использовании поддерживалось актуальное состояние новой таблицы.</li><li>Начните использовать новую таблицу.</li></ul><p>Другие подходы, основанные на использовании представлений (views) и/или правил (RULE), также могут вам подойти, всё зависит от производительности, которая вам необходима.</p><h2>Операции над столбцами</h2><p>Обратите внимание: установка ограничений, накладываемых на столбцы (например NOT NULL), и прочих ограничений (например EXCLUDES) описана в <a href="https://tproger.ru/#constraints">отдельной части статьи</a>.</p><h3>Добавить столбец</h3><p>Добавление столбца в существующую таблицу обычно требует короткой блокировки уровня ACCESS EXCLUSIVE на таблице на то время, пока обновляются системные таблицы каталогов (catalog tables).</p><p>Значения по умолчанию. Установка значения по умолчанию одновременно с созданием столбца заблокирует таблицу на время установки значений. Вместо этого следует:</p><ul><li>Добавить новый столбец (без значений по умолчанию).</li><li>Назначить столбцу значение по умолчанию.</li><li>Заполнить этим значением уже существующие строки по отдельности.</li></ul><p>Обратите внимание: В недавно выпущенном PostgreSQL 11 эти советы больше не актуальны для неволатильных значений по умолчанию. Теперь добавление столбца со значением по умолчанию требует только обновления таблиц каталогов, а все обращения к строкам без значения будут магическим образом <a href="https://www.depesz.com/2018/04/04/waiting-for-postgresql-11-fast-alter-table-add-column-with-a-non-null-default/">возвращать</a> нужное значение.</p><p>Ограничения not null. Добавление столбца с ограничением NOT NULL возможно только в двух случаях: если в таблице нет строк или если был указан DEFAULT. Первый случай тривиален — потребуется только изменение каталога. Во втором же случае следует проделать все описанные выше действия для значений по умолчанию.</p><p>Заметьте: После добавления нового столбца все запросы вида SELECT * FROM ... начнут возвращать новый столбец. Важно, чтобы код, который будет работать с этой таблицей, мог безопасно обработать новый столбец. Лучше просто не использовать *, всегда указывая столбцы явно.</p><h3>Изменить тип столбца</h3><p>Обычно изменение типа столбца приводит к полной блокировке всей таблицы до тех пор, пока все строки не будут обновлены в соответствии с новым типом. Однако есть несколько исключений:</p><ul><li>Приведение VARCHAR к типу TEXT (<a href="https://www.postgresql.org/message-id/flat/E1PoFlG-0005Et-J1%40gemulon.postgresql.org">начиная</a> с версии 9.1); а точнее всегда, когда старый тип бинарно совместим с новым типом и для преобразования не требуется никаких фактических операций.</li><li>Старый тип является частным случаем нового (<a href="https://www.postgresql.org/message-id/flat/E1PpCi5-0005St-N1%40gemulon.postgresql.org">начиная</a> с версии 9.1).</li><li>Когда увеличивается или удаляется заданное ограничение на длину или точность: например VARCHAR(5) → VARCHAR(10) и VARCHAR(5) → VARCHAR (<a href="https://www.depesz.com/2012/02/14/waiting-for-9-2-more-rewrite-less-alter-table-alter-types/">начиная</a> с версии 9.2).</li></ul><p>Обратите внимание: Несмотря на то что некоторые из исключений выше были введены ещё в версии 9.1, изменение типа индексируемого столбца в этой версии всегда приводит к переписыванию индекса. Начиная с версии 9.2, индекс не переписывается, если не переписывалось содержимое таблицы. Если вы хотите удостовериться, что ваше изменение не инициирует перезапись, вы можете сделать запрос к pg_class3 и проверить, что столбец relfilenode не изменился.</p><p>Если вам требуется изменить тип столбца, и описанные выше исключения к вашему случаю не относятся, то:</p><ul><li>Добавьте новый столбец new_&lt;column&gt;.</li><li>Осуществляйте запись одновременно в оба столбца (например, с помощью триггеров BEFORE INSERT/UPDATE).</li><li>Заполните новый столбец копиями значений из старого.</li><li>Переименуйте &lt;column&gt; в old_&lt;column&gt;, а new_&lt;column&gt;, соответственно, в &lt;column&gt;; делайте это внутри единой транзакции и явного выражения LOCK &lt;table&gt;.</li><li>Удалите старый столбец.</li></ul><h3>Удалить столбец</h3><p>Удалять столбец нужно с крайней осторожностью. Для обновления каталога оно требует полной блокировки таблицы, но не влечёт за собой физического изменения строк. Если в настоящее время столбец не используется, вы можете безопасно удалить его. Важно, однако, проверить, что на этот столбец не ссылаются никакие зависимые объекты (которые небезопасно удалять). В частности, любые индексы, использующие столбец, должны быть удалены отдельно с использованием безопасного DROP INDEX CONCURRENTLY. В противном случае, они будут автоматически удалены вместе со столбцом, и всё это время будет действовать блокировка уровня ACCESS EXCLUSIVE. Чтобы проверить, есть ли у вас такие объекты, вы можете сделать запрос к pg_depend4.</p><p>Прежде чем запускать ALTER TABLE ... DROP COLUMN ... на продакшне, стоит удостовериться, что все ссылки на этот столбец в документации и коде были окончательно убраны. Это позволит безопасно откатиться к релизам, выпущенным до того, как был удалён столбец.</p><p>Заметьте: Удаление столбца потребует от вас обновления всех представлений, триггеров, функций и т. д., которые были завязаны на этот столбец.</p><h2>Операции над индексами</h2><h3>Создать индекс</h3><p>Если вы просто запустите CREATE INDEX ..., то получите блокировку уровня ACCESS EXCLUSIVE на всей индексируемой таблице. А вот если вы выполните CREATE INDEX CONCURRENTLY ... , то блокировка будет всего лишь уровня SHARE UPDATE EXCLUSIVE. Правда, вместо одного сканирования таблицы придётся выполнить два. При уровне блокировки во втором случае будут разрешены и чтение, и запись в таблицу.</p><p>Предостережения:</p><ul><li>Несколько созданий индексов, выполняющиеся параллельно на одной таблице, не завершат выполнение ни одного из CREATE INDEX CONCURRENTLY ...  до тех пор, пока самый медленный из них ещё работает.</li><li>CREATE INDEX CONCURRENTLY ... не может быть выполнен внутри транзакции, вместо этого транзакциями неявно управляет PostgreSQL. Из-за этого никакие auto-vacuum’ы не смогут очистить ненужные кортежи, которые появились после начала построения индекса, и до завершения этого процесса. Если у таблицы большой объём изменений (особенно плохо, если сама таблица при этом мала), это может привести к крайне неоптимальному времени выполнения запроса.</li><li>CREATE INDEX CONCURRENTLY ... завершит выполнение только после того, как завершатся все транзакции, использующие таблицу.</li></ul><h3>Удалить индекс</h3><p>Стандартное выражение DROP INDEX ... получает ACCESS EXCLUSIVE на всей таблице на всё время удаления индекса. Для небольших индексов это может не быть проблемой — это должна быть весьма короткая операция. Однако для огромных индексов работа с файловой системой может занять значительное время. Нам на помощь придёт DROP INDEX CONCURRENTLY ..., которая потребует блокировку уровня SHARE UPDATE EXCLUSIVE; запись и чтение будут продолжаться, пока мы удаляем индекс.</p><p>Подводные камни использования DROP INDEX CONCURRENTLY ...:</p><ul><li>Этот запрос не может быть использован для удаления индекса, который поддерживал какое-либо ограничение (например PRIMARY KEY или UNIQUE).</li><li>Он не может быть использован как часть транзакции, ими управляет PostgreSQL “под капотом”. Из-за этого никакие auto-vacuum’ы не смогут очистить ненужные кортежи, которые появились после начала построения индекса, и до завершения этого процесса. Если у вас есть таблица с большим объёмом изменений (особенно плохо, если сама таблица при этом мала) это может привести к крайне неоптимальному времени выполнения запроса.</li><li>Запрос завершит выполнение только после того, как завершатся все транзакции, использующие таблицу.</li></ul><p>Обратите внимание: DROP INDEX CONCURRENTLY ... был добавлен <a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commitdiff;h=8cb53654dbdb4c386369eb988062d0bbb6de725e">только в Postgres 9.2</a>. Если вы всё ещё работаете с версией 9.1 или ниже, вы можете добиться примерно такого же результата, если отметите индекс как некорректный (invalid) и не готовый к записи; затем вам нужно будет сбросить буфер с помощью расширения pgfincore. После этого можно просто удалять индекс.</p><h3>Переименовать индекс</h3><p>ALTER INDEX ... RENAME TO ... требует блокировку уровня ACCESS EXCLUSIVE на переименовываемом индексе, блокируя чтение и запись в соответствующую таблицу. Однако <a href="https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=1b5d797cd4f7133ff0d18e123fcf41c67a5a7b0b">коммит</a>, который должен стать частью PostgreSQL 12 понижает это требование до SHARE UPDATE EXCLUSIVE.</p><h3>Произвести переиндексацию</h3><p>REINDEX INDEX ...  также требует ACCESS EXCLUSIVE на индексе. Чтобы этого не допускать, рекомендуется следующий алгоритм:</p><ul><li>Создать новый индекс, как это описано выше, который повторял бы существующий.</li><li>Удалить старый индекс, наименее затратным способом (описан выше).</li><li>Переименовать новый индекс, чтобы он повторял имя старого.</li></ul><p>Заметьте: Если индекс, который вам необходимо перестроить, содержал ограничения, не забудьте добавить их и в новый индекс (как это сделать, мы как раз рассмотрим <a href="https://tproger.ru/#constraints">в следующей части</a>).</p><h2>Ограничения</h2><h3>NOT NULL</h3><p>Удаление существующего ограничения NOT NULL из столбца требует полной блокировки таблицы. Это не так существенно, так как выполняется простое обновление каталога.</p><p>А вот добавление ограничения NOT NULL к существующему столбцу требует ACCESS EXCLUSIVE на время проведения полного скана таблицы, чтобы удостовериться, что в ней нет null-значений. Вместо этого вам следует:</p><ul><li>Добавить <a href="https://www.postgresql.org/docs/current/ddl-constraints.html#DDL-CONSTRAINTS-CHECK-CONSTRAINTS">ограничение проверки</a> CHECK, которое бы требовало от значений столбца не быть null-ами. Сделать это можно с помощью ALTER TABLE &lt;table&gt; ADD CONSTRAINT &lt;name&gt; CHECK (&lt;column&gt; IS NOT NULL) NOT VALID;. Здесь NOT VALID сообщает PostgreSQL, что нет необходимости проводить полную проверку, чтобы удостовериться, что все строки соответствуют условию.</li><li>Вручную проверить, что все строки содержат значения, отличные от null.</li><li>Валидировать наложенное ограничение с помощью ALTER TABLE &lt;table&gt; VALIDATE CONSTRAINT &lt;name&gt;;. Выполнение этого выражения заблокирует получение других EXCLUSIVE блокировок таблицы, но не будет мешать записи или чтению.</li></ul><p>Бонус: сейчас в работе находится <a href="https://www.postgresql.org/message-id/flat/7a6a8eb5-a36c-239a-1724-9fc8a2f29115%40postgrespro.ru#40ee6caa2479b5750490e456bec91165">патч</a> (и, возможно, он войдёт в релиз PostgreSQL 12), который позволит создавать ограничение NOT NULL без полного просмотра таблицы при ограничении CHECK вроде того, что мы создали выше.</p><h3>Внешний ключ</h3><p>ALTER TABLE ... ADD FOREIGN KEY требует блокировку SHARE ROW EXCLUSIVE (по крайней мере с версии 9.5) на обеих таблицах — и на изменяемой, и на той, на которую мы ссылаемся. Такая блокировка, конечно, не будет блокировать запросы SELECT, однако длительный запрет на внесение изменений тоже неприемлем.</p><p>Чтобы избежать длительной блокировки, можно поступить следующим образом:</p><ul><li>ALTER TABLE ... ADD FOREIGN KEY ... NOT VALID: добавит внешний ключ и начнёт применять ограничение ко всем новым выражениям INSERT/UPDATE. При этом существующие строки не будут проверены на соответствие ограничению. Это операция тоже требует SHARE ROW EXCLUSIVE, но лишь на очень короткое время.</li><li>ALTER TABLE ... VALIDATE CONSTRAINT &lt;constraint&gt; проверит все существующие строки на соответствие указанному ограничению. Проверка требует  только SHARE UPDATE EXCLUSIVE , поэтому может работать параллельно с чтением данных и записью.</li></ul><h3>Ограничение проверки (CHECK)</h3><p>ALTER TABLE ... ADD CONSTRAINT ... CHECK (...) требует блокировку уровня ACCESS EXCLUSIVE. Однако, как и в случае с внешними ключами, эту операцию можно разделить на две:</p><ul><li>ALTER TABLE ... ADD CONSTRAINT ... CHECK (...) NOT VALID добавит ограничение проверки и начнёт применять ограничение ко всем новым выражениям INSERT/UPDATE. При этом существующие строки проверяться не будут. Это операция требует ACCESS EXCLUSIVE.</li><li>ALTER TABLE ... VALIDATE CONSTRAINT &lt;constraint&gt; проверит все существующие строки. Проверка требует SHARE UPDATE EXCLUSIVE на таблице. Если ограничение ссылается на другую таблицу, на ней будет установлена блокировка уровня ROW SHARE. Напомним, она лишь откладывает операции, которые требуют полной блокировки таблицы.</li></ul><h3>Ограничение уникальности (UNIQUE)</h3><p>ALTER TABLE ... ADD CONSTRAINT ... UNIQUE (...) требует блокировку уровня ACCESS EXCLUSIVE. И снова делим операцию на две:</p><ul><li>Конкурентно создайте индекс с ограничением на уникальность (как <a href="https://tproger.ru/#concurrentindex">описано</a> выше). Это действие само по себе будет требовать уникальности значений. Однако, если вам нужно именно ограничение в смысле constraint (или первчиный ключ), то вы можете добавить его следующим шагом.</li><li>ALTER TABLE ... ADD CONSTRAINT ... UNIQUE USING INDEX &lt;index&gt; создаст ограничение, используя уже существующий индекс. Операция всё ещё требует ACCESS EXCLUSIVE, но лишь для быстрых операций над каталогом.</li></ul><p>Обратите внимание: если вы указываете PRIMARY KEY вместо UNIQUE, то все столбцы, которые могли содержать null‘ы получат ограничение NOT NULL. Это потребует полного скана таблицы, и на данный момент этого никак нельзя избежать. Детали описаны в <a href="https://tproger.ru/#notnull">разделе</a> про NOT NULL.</p><h3>Ограничение-исключение (EXCLUDE)</h3><p>ALTER TABLE ... ADD CONSTRAINT ... EXCLUDE USING ... требует блокировки ACCESS EXCLUSIVE. Если вы добавите ограничение исключительности, то это потянет за собой создание индекса, и, к сожалению, не получится использовать уже сформированный индекс (как мы делали с ограничением уникальности выше).</p><h2>Перечисляемые типы</h2><p>CREATE TYPE &lt;name&gt; AS (...) и DROP TYPE &lt;name&gt; (после проверки, что тип нигде не используются) могут быть безопасно выполнены без всяких блокировок.</p><h3>Изменение используемых значений</h3><p>В PostgreSQL добавили выражение ALTER TYPE &lt;enum&gt; RENAME VALUE &lt;old&gt; TO &lt;new&gt;. Оно не требует блокировки таблиц, которые используют перечислимый тип.</p><h3>Удаление значений</h3><p>Перечислимые типы хранятся в виде целых чисел. Пропуски в диапазоне допустимых значений не поддерживаются. Из-за этого удаление одного допустимого значения привело бы к необходимости изменять данные во всех строках, которые использовали этот тип. PostgreSQL в настоящее время не поддерживает удаление одного значения из существующего перечислимого типа.</p><h2>Библиотека для Ruby on Rails</h2><p>Braintree Payments, в дополнение к статье, выложили исходный код своей библиотеки для Ruby on Rails — <a href="https://github.com/braintree/pg_ha_migrations">pg_ha_migrations</a>. Этот gem позволяет безопасно использовать DDL в проектах, которые используют Ruby on Rails и/или ActiveRecord. Её основная задача — позволить явно указывать способ выполнения операции, выбирая между различными видами издержек. Подробнее можно прочитать в <a href="https://github.com/braintreeps/pg_ha_migrations/blob/master/README.md">README проекта</a>.</p><h2>Примечания</h2><p>1 Вы можете получить активные запросы, которые долго исполняются, выполнив следующий системный запрос:</p><p>2 Посмотреть внутреннюю статистику PostgrSQL об использовании заданной таблицы можно, выполнив следующий запрос:</p><p>3 Если вы хотите узнать, вызывает ли DDL перезапись связанных объектов, вам стоит посмотреть меняются ли значения relfilenode после выполнения следующего выражения:</p><p>4 Следующее выражение поможет вам найти любые зависимые от столбца объекты (в частности, индексы):</p>]]></content:encoded>
    </item>
  </channel>
</rss>