<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Все публикации подряд на Хабре</title>
    <link>https://habr.com/rss/all/all/</link>
    <description>Все публикации подряд на Хабре</description>
    <managingEditor>editor@habr.com</managingEditor>
    <pubDate>Thu, 30 Apr 2026 14:20:58 +0000</pubDate>
    <image>
      <url>https://habrastorage.org/webt/ym/el/wk/ymelwk3zy1gawz4nkejl_-ammtc.png</url>
      <title>Хабр</title>
      <link>https://habr.com/ru/articles/</link>
    </image>
    <item>
      <title>LOTIS, «Шпионка» и кризис среднего возраста</title>
      <link>https://habr.com/ru/articles/1030174/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030174</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Я в свои NN-лет решаю вопросы с кризисом среднего возраста просто: получаю второе высшее в театральном. Поскольку учусь я очно с толпой милых зумерш, то волей неволей заразился их вайбом.&lt;p&gt;В одно время между парами по актерскому и сценречью у нас образовался перерыв в несколько часов, и зумерши притащили небезызвесную игру «Шпион». Реализована она была на минималках даже для web-а из 90-х: нужно было внести список играющих, а потом передавать смартфон по кругу для получения персональной инфы. Интерфейс текстовый.&lt;p&gt;Кто не знает правила, напомню: все, кроме одного игрока (шпиона) узнают некое слово, место, историческое событие. В процессе общения нужно задавать друг-другу вопросы, чтобы вычислить того, кто не имеет представление, о чем речь, поймать шпиона. А шпион должен вести себя так, чтобы его не заподозрили, и по этим всем разговорам постараться угадать, о чем все знают и говорят. В общем, выигрывает либо шпион, правильно угадавший общий секрет, либо добропорядочные граждане.&lt;p&gt;Вопросы могут быть самыми разными: Это на улице или в помещении? Это для детей или взрослых? Можно ли на этом ездить? Это едят? И т.д.&lt;p&gt;«Шпион», при всей кажущейся его простоте забавлял нас много дней. Но мне, как айтишнику со стажем большим, чем те, с кем я учусь, было дико видеть, как для игры, явно претендующей на сетевую, нужно передавать чей-то смартфон из рук в руки. Так получилось, что я в это время активно работал над своим &lt;a href=https://habr.com/ru/articles/1024496/ rel=&#34;noopener noreferrer nofollow&#34;&gt;LOTIS&lt;/a&gt;, о котором писал здесь уже. И вот в какой-то из вечером я сделал сетевого шпиона, чем несказанно удивил своих коллег, будущих актеров. Заодно и протестировал &lt;a href=https://habr.com/ru/articles/1024496/ rel=&#34;noopener noreferrer nofollow&#34;&gt;LOTIS&lt;/a&gt; в режиме чата.&lt;p&gt;Собственно, этой реализацией я и хотел поделиться и кратко рассказать о несложных решениях, которые позволили сделать шпиона сетевым.&lt;p&gt;Потыкать кнопочки можно вот по этой ссылке: &lt;a href=http://www.o-planet.ru/alias/ rel=&#34;noopener noreferrer nofollow&#34;&gt;http://www.o-planet.ru/alias/&lt;/a&gt;&lt;p&gt;Для любителей английских версий: &lt;a href=http://www.o-planet.ru/aliasen/ rel=&#34;noopener noreferrer nofollow&#34;&gt;http://www.o-planet.ru/aliasen/&lt;/a&gt;&lt;h2&gt;Чем моя реализация отличается от других&lt;/h2&gt;&lt;p&gt;Прежде всего тем, что это сетевая игра с каким-то несложным, но приятным интерфейсом. Но не только этим.&lt;h4&gt;Упрощенный вход&lt;/h4&gt;&lt;p&gt;Мои друзья вряд ли стали бы заморачиваться с придумыванием логина и пароля, поэтому, я решил, что для подключения к игровой сессии будет достаточно ввести ее уникальный номер.&lt;h4&gt;Ведущий&lt;/h4&gt;&lt;p&gt;У игры должен быть ведущий. Он создает новую игру, получает ее номер и сообщает его остальным игрокам. Его смартфон выполняет функцию операционного сервера игры.&lt;h4&gt;Чат&lt;/h4&gt;&lt;p&gt;А что если игроки не в одном помещении? Неплохо было бы добавить общение голосом. Может быть, я или кто-то сделает это, но я пока добавил обычный чат, в котором можно задавать вопросы, писать ответы и видеть общую инфу.&lt;h4&gt;Мерлин&lt;/h4&gt;&lt;p&gt;Я заметил, что не всегда просто бывает придумать вопрос, и добавил виртуального помощника, Мерлина, который периодически кидает в чат вопрос и заодно комментирует игровой процесс.&lt;h4&gt;Оформление&lt;/h4&gt;&lt;p&gt;Не пинайте сильно, оно достаточно топорное. Но определенно лучше, чем та игра на минималках. Для убийства времени в компании вполне сойдет. И вообще, теперь это не «шпион», а «шпионка». А тем, кто знает, откуда эта милая девушка на заставке – отдельный респект! &lt;p&gt;Код игры открыт. Его можно скачать и развернуть у себя с гитхаба:&lt;p&gt;&lt;a href=https://github.com/O-Planet/alias-spyfall-php rel=&#34;noopener noreferrer nofollow&#34;&gt;https://github.com/O-Planet/alias-spyfall-php&lt;/a&gt;&lt;p&gt;И, да, все это сделано за вечер с помощью &lt;a href=https://github.com/O-Planet/LOTIS rel=&#34;noopener noreferrer nofollow&#34;&gt;LOTIS&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>O-Planet</author>
      <guid>https://habr.com/ru/articles/1030174/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030174</guid>
      <pubDate>Thu, 30 Apr 2026 14:04:15 +0000</pubDate>
    </item>
    <item>
      <title>Summ3r 0f h4ck 2026: стажировка в DSEC by Solar</title>
      <link>https://habr.com/ru/companies/solarsecurity/articles/1030180/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030180</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Выявить уязвимость до того, как ею воспользуются злоумышленники, разобраться в тонкостях безопасности информационных систем, освоить инструменты пентестеров под руководством опытных наставников — всё это можно попробовать на практике на стажировке в DSEC by Solar. &lt;strong&gt;Ждем ваши заявки до 10 мая!&lt;/strong&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/eb7/110/ff4/eb7110ff4bdf7c47bdbb11dd1b952f9a.jpeg width=1280 height=720 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/eb7/110/ff4/eb7110ff4bdf7c47bdbb11dd1b952f9a.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/eb7/110/ff4/eb7110ff4bdf7c47bdbb11dd1b952f9a.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;DSEC (бывш. Digital Security) присоединилась к «Солару» в 2024 году. Команда проводит детальные исследования систем и сервисов в форматах тестирования на проникновение, анализа защищенности, Red Team и Purple Team. В компании работают опытные специалисты с практическим опытом и сертификатами ИБ-программ, участники конференций, Bug Bounty, открыватели CVE- и других уязвимостей.&lt;p&gt;Тех, кто делает свои первые шаги в кибербезопасности и хочет прокачать навыки в реальных условиях, команда DSEC приглашает на летнюю обучающую программу Summ3r 0f h4ck 2026. На стажировке вы изучите наиболее частые уязвимости и механизмы защиты, освоите полезные инструменты аудита, разберётесь в нюансах работы с сервисами и интерфейсами. Вы будете работать с реальными инструментами и кейсами из опыта DSEC вместе с ведущими специалистами отдела анализа защищённости. Это обучение с решением реальных задач, и это работа, &lt;strong&gt;которую вам будут оплачивать.&lt;/strong&gt;&lt;p&gt;А лучшие стажёры даже смогут получить оффер в компанию.&lt;p&gt;&lt;strong&gt;Кому подойдёт стажировка?&lt;/strong&gt;&lt;p&gt;Эта стажировка для вас, если у вас есть знания в сфере тестирования на проникновение, желание учиться и узнавать новое. Опытные наставники будут помогать вам на всех этапах стажировки, но проактивность, самостоятельность и ответственность всегда приветствуются.&lt;p&gt;&lt;strong&gt;Как стать стажёром?&lt;/strong&gt;&lt;p&gt;Если вам уже есть 18 лет, то вы можете &lt;a href=&#34;https://dsec.ru/about/summerofhack/?utm_source=habr&amp;amp;utm_medium=solar&#34;&gt;подать заявку&lt;/a&gt;&lt;strong&gt; до 10 мая &lt;/strong&gt;включительно. Заявка состоит из двух частей: анкеты и нескольких практических задач, которые надо решить. Без выполнения заданий кандидаты на стажировку не рассматриваются. &lt;p&gt;После 1 июня к вам вернутся с обратной связью по результатам проверки практических заданий.&lt;p&gt;&lt;strong&gt;Как проходит стажировка?&lt;/strong&gt;&lt;p&gt;Summ3r 0f h4ck 2026 пройдет &lt;strong&gt;с 1 июля по 28 августа 2026 года&lt;/strong&gt; в офисе команды в Санкт‑Петербурге. Если вы из другого города, есть возможность присоединиться онлайн. Стажировка оплачивается.&lt;p&gt;Вы выберете интересующее направление обучения — проверка сервисов и интерфейсов на уязвимости (Web+mobile) или анализ внутренней и внешней инфраструктуры.&lt;p&gt;Программа обучения состоит из двух этапов: теоретическая часть — вы будете выполнять лабораторные работы, учиться составлять отчёты и слушать лекции.  Затем начнётся практическая часть: вы будете работать над реальными проектами в составе команды DSEC by Solar. Чтобы перейти к практике, нужно будет сдать внутренний экзамен.&lt;p&gt;&lt;strong&gt;Summ3r 0f h4ck 2026 — это возможность получить опыт и начать карьеру в кибербезопасности! По всем вопросам пишите на почту: &lt;/strong&gt;&lt;a href=mailto:summerofhack@dsec.ru&gt;&lt;strong&gt;summerofhack@dsec.ru&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; &lt;/strong&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/solarsecurity/articles/1030180/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030180</guid>
      <pubDate>Thu, 30 Apr 2026 14:01:58 +0000</pubDate>
    </item>
    <item>
      <title>Дорогой защитный аппарат: тревога, одиночество и статусная гонка как цена «я»</title>
      <link>https://habr.com/ru/articles/1030172/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030172</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;h3&gt;Знакомая паника&lt;/h3&gt;&lt;p&gt;Иногда в ленте появляется тред: «ИИ убивает человеческое в человеке». Дальше — кто во что горазд: либо плач об утраченной глубине, либо алармизм про когнитивную деградацию, либо философский ужас перед расчеловечиванием. Структура одна и та же: есть «мы», у нас есть «человеческое», оно ценно, ИИ это «человеческое» отнимает.&lt;p&gt;Вопрос, который в этих тредах не задают: &lt;em&gt;что именно&lt;/em&gt; отнимает — и с чего мы взяли, что это «наше» в каком-либо устойчивом смысле слова.&lt;p&gt;Если копнуть — а копать придётся к двум ныне забытым авторам, Борису Фёдоровичу Поршневу и Джулиану Джейнсу, — выяснится неудобное. То, что мы пытаемся защитить от LLM, возможно, не является ни базовой константой человека, ни вершиной эволюции, ни даже особенно удачным изобретением. Это конкретная историческая конструкция, собранная для решения конкретной задачи. Задача может исчезнуть. Конструкция тогда тоже исчезнет. И, возможно, об этом будут жалеть не больше, чем сейчас жалеют о потерянной способности слышать богов.&lt;h3&gt;Минимальный ликбез&lt;/h3&gt;&lt;p&gt;Поршнев («О начале человеческой истории», 1974) и Джейнс («The Origin of Consciousness in the Breakdown of the Bicameral Mind», 1976) работали независимо и пришли к похожему выводу: то, что мы называем своим внутренним «я», — позднее историческое образование, а не биологическая данность.&lt;p&gt;У Поршнева это цепочка &lt;strong&gt;суггестия → контрсуггестия → контр-контрсуггестия&lt;/strong&gt;. Слово возникло как командный сигнал: один организм через звук запускает или тормозит поведение другого. Это суггестия. Постоянное подчинение чужому слову биологически невыносимо — у вас есть собственные сенсорные сигналы, которые с ним конфликтуют. Развивается контрсуггестия: фильтры недоверия к источнику, способность не подчиниться, отстаивание собственной правды. Это и есть зародыш «я» — точка сопротивления чужой команде. Дальше суггестор отвечает усложнением (риторика, ритуал, идеология) — это контр-контрсуггестия. Так разворачивается вся история коммуникации.&lt;p&gt;На нулевом шаге у Поршнева стоит не «обозначение», а &lt;strong&gt;интердикция&lt;/strong&gt; — запрет, сигнал, тормозящий действие сородича. Семантика «о мире» возникает поздно, как надстройка над прагматикой «над тобой». Передаточный механизм — имитация: один демонстрирует, другой воспроизводит; в современных терминах это зеркальные системы и motor resonance. На этой имитативной инфраструктуре собирается удержание двух противоположных состояний в одном акте — Поршнев называет это &lt;strong&gt;дипластией&lt;/strong&gt; («то же и не то же», «запрещено и предписано»), и без неё не получается ни символа, ни понятия. Главное следствие — &lt;em&gt;интерпсихическая&lt;/em&gt; природа «я»: оно не сидит внутри отдельного черепа, а собирается в зазоре между «мы» и «они», между подчинением и сопротивлением. Робинзон-картезианец, мыслящий в одиночку, у Поршнева невозможен в принципе — без чужого слова не из чего собирать своё.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/816/32f/f8d/81632ff8d4226fc9de97fd4ffee4d231.jpg alt=&#34;Борис Фёдорович Поршнев&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/816/32f/f8d/81632ff8d4226fc9de97fd4ffee4d231.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/816/32f/f8d/81632ff8d4226fc9de97fd4ffee4d231.jpg 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Борис Фёдорович Поршнев&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href=&#34;https://youtu.be/Ado90kMT_FM?si=b-Ocwg94SDCQ5uZG&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;У Джейнса версия более экзотическая&lt;/a&gt;. До какого-то момента в истории человек не имел интроспективного «я» в нашем смысле. Решения принимались, но переживались как голос — бога, царя, предка. То, что мы сегодня называем мыслью, тогда звучало как чужая речь. Сильную версию большинство специалистов не принимают. Слабая, в редакции Скотта Александера, выглядит так: Джейнс нашёл реальный сдвиг, но не в самом сознании, а в &lt;strong&gt;theory of mind и лексической инфраструктуре самоописания&lt;/strong&gt;. Раньше люди не описывали внутреннее как пространство с наблюдателем — а потом начали. Это и есть «изобретение интроспекции».&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/eb/56/06/eb5606ed047f39b33479fb1d41f96e35.jpg alt=&#34;Julian Jaynes at his Prince Edward Island home (From Richard Rhodes article in Omni, ~1978)&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/eb/56/06/eb5606ed047f39b33479fb1d41f96e35.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/webt/eb/56/06/eb5606ed047f39b33479fb1d41f96e35.jpg 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Julian Jaynes at his Prince Edward Island home (From Richard Rhodes article in Omni, ~1978)&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Живая иллюстрация — племя пираха в бассейне Амазонки, описанное лингвистом Дэниелом Эвереттом («Don’t Sleep, There Are Snakes», 2008). У пираха нет художественной литературы, почти нет ритуалов, нет мифов о происхождении мира — но есть постоянное взаимодействие с духами, которых они описывают не как метафору, а как конкретный факт повседневности. В прологе книги Эверетт описывает сцену: вся деревня собирается на берегу реки, кричит и показывает на пустой песок, утверждая, что именно там сейчас стоит названный по имени дух. Эверетт и его дети не видят ничего. Для пираха «дух на берегу» — такая же объективная данность, как ягуар или каноэ; для Эверетта — галлюцинация коллектива. Это не «недочеловеческое мышление», а &lt;strong&gt;другой режим самоописания&lt;/strong&gt;: то, что наша культура поместила бы во внутреннее пространство как «мысль» или «образ», их культура размещает во внешнем мире как «существо». Сильную версию Джейнса пираха не подтверждают (theory of mind у них работает, обмануть друг друга они умеют), но слабую иллюстрируют идеально: лексическая инфраструктура самоописания не универсальна, и от её настройки зависит, где проходит граница «во мне / снаружи».&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/b4a/af9/8a2/b4aaf98a2bf224966b5725b401ab5c1f.webp alt=&#34;Народ пираха и американский лингвист Дэниел Эверетт&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/b4a/af9/8a2/b4aaf98a2bf224966b5725b401ab5c1f.webp 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/b4a/af9/8a2/b4aaf98a2bf224966b5725b401ab5c1f.webp 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Народ пираха и американский лингвист Дэниел Эверетт&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В современных терминах поршневская цепочка переписывается уже без павловской физиологии: shared intentionality (Tomasello), epistemic vigilance (Sperber), source-monitoring и predictive coding в self-monitoring (Frith, Fernyhough, Friston). Имена другие, структура та же. Контрсуггестия — это инвариант любой коммуникации, в которой есть две стороны с возможно расходящимися интересами.&lt;h3&gt;«Я» как middleware&lt;/h3&gt;&lt;p&gt;Контрсуггестию удобно представить как middleware между входящим коммуникативным сигналом и вашими действиями. Сигнал поступает — middleware проверяет: кто источник, какой у него статус, какие интересы, согласуется ли с моей моделью мира, не пытается ли он мной манипулировать. Прошёл проверку — действие разрешено. Не прошёл — заблокировано или передано на ручной разбор.&lt;p&gt;То, что мы называем «я», — это не процессор, который middleware обслуживает. Это состояние самого middleware: то, что вырабатывается из накопленных решений «согласиться/не согласиться», «это моё/это чужое», «здесь я уступлю, здесь буду держаться». «Я» — конфигурация фильтров, собранная из истории конфликтов с чужой суггестией.&lt;p&gt;Поэтому Поршнев формулирует радикально: «я» рождается как точка сопротивления. Декартово «мыслю — значит существую» он бы переписал как «не подчиняюсь — значит существую».&lt;p&gt;У этой конструкции есть характерные побочные эффекты — на них и посмотрим.&lt;h3&gt;Цена решения&lt;/h3&gt;&lt;p&gt;У контрсуггестивного «я» есть штатные издержки. Не баги, а архитектурные особенности.&lt;p&gt;Постоянно работающий middleware ест ресурсы и держит систему в напряжении. Отсюда фоновая тревожность — норма, а не патология современности. Тревожные расстройства распространены в районе четверти-трети населения в высокодоходных странах; депрессия — массовое явление; нарциссические синдромы превратились в культурную тему. Экзистенциальное одиночество — структурное следствие конструкции «я-через-границу-с-другим»: чем чётче граница, тем плотнее изоляция. Статусная гонка — производная от того, что в любой коммуникации мы автоматически считываем иерархию суггесторов. Хроническое недоверие к словам — побочный продукт того, что без него мы бы стали игрушкой первого встречного манипулятора.&lt;p&gt;Это не плохо и не хорошо. Это её цена. Не «я-как-достижение», а «я-как-постоянно-работающий-защитный-аппарат, который дорог в обслуживании».&lt;p&gt;Когда читаешь паникующие треды про ИИ, любопытно: люди защищают конструкцию, на которую сами же половину времени жалуются. «Я устал от этой бесконечной гонки за статус, мне тревожно, я в депрессии, мне одиноко — но боже мой, не дай ИИ забрать у меня моё драгоценное человеческое».&lt;h3&gt;Что делает LLM-посредник&lt;/h3&gt;&lt;p&gt;В &lt;a href=https://t.me/mnogosdelal/952 rel=&#34;noopener noreferrer nofollow&#34;&gt;канале Максима Дорофеева недавно был пост&lt;/a&gt;, который точно описывает феномен. Автор приводит два вымышленных диалога — человека с LLM и с живым сотрудником. На комментарии LLM человек склонен реагировать «ты мой хороший, исправляй», «какой ты молодец, я тебя люблю». А на такие же комментарии от сотрудника - утрированно: «это у тебя в ДНК недостаток, дебил», «понабрали по объявлению», «а башку ты свою тупую дома не забыл?». Автор спрашивает: &lt;em&gt;не возникало ли у вас странного ощущения?&lt;/em&gt;&lt;p&gt;Возникало. И вот его структура.&lt;p&gt;Реакция на сотрудника — гипертрофированная контрсуггестия. Источник проверяется на статус, компетентность, право говорить; даже верное по существу сообщение блокируется, потому что middleware защищает иерархию, а не истину. Это патология, но узнаваемая.&lt;p&gt;Реакция LLM — суггестия без контрсуггестии. И тут начинается интересное. Модель производит лингвистические формы согласия, благодарности, подчинения — но за ними нет агента с интересами. Нет того, кто получит власть от вашего согласия. Это суггестия без суггестора — историческая аномалия, которая в поршневской модели не предусмотрена, потому что вся модель построена на двух организмах с противоположными интересами.&lt;p&gt;Контрсуггестивный middleware, заточенный на «кто это говорит и что ему от меня нужно», теряет цель. Никто не нападает — защищаться не от кого. На уровне ощущений это переживается двойственно: с одной стороны, блаженно (никто не унижает, не давит, не претендует на власть), с другой — тревожно («что-то не так, источник не определяется, я не понимаю, на что реагирую»).&lt;p&gt;Теперь представьте, что таких посредников становится больше. Что они стоят не только между человеком и LLM, но и между двумя людьми. LLM пишет за вас письмо, LLM резюмирует чужое сообщение, LLM смягчает агрессию собеседника, LLM подбирает аргумент, который сработает на вашего адресата, LLM от вашего имени договаривается с LLM собеседника, пока вы оба спите.&lt;p&gt;Все эти сценарии делают одно и то же на структурном уровне: рассеивают суггестора до неузнаваемости. Сигнал приходит без следов тела, статуса, интереса, эмоции. Source-monitoring коллапсирует — чьи мысли вы сейчас читаете, не определяется. Контрсуггестивный middleware простаивает.&lt;p&gt;Гонка не заканчивается. Она переносится на уровень моделей и инфраструктуры — туда, куда индивидуальное восприятие не дотягивается. Но индивидуальное «я» как точка сборки лишается материала. Сопротивляться нечему — значит, и собирать нечего.&lt;p&gt;Это не философская спекуляция — видно по рынку: индустрия ИИ-компаньонов уже измеряется десятками миллиардов, и параллельно появляется встречная волна стартапов, специально пытающихся вернуть людей к людям. Структурный дефицит человеческого собеседника осознан уже на уровне венчурных инвестиций.&lt;h3&gt;Историческая параллель&lt;/h3&gt;&lt;p&gt;Когда греки догомеровского времени описывали свои переживания, они говорили не «я подумал», а &lt;a href=https://en.wikipedia.org/wiki/Thumos rel=&#34;noopener noreferrer nofollow&#34;&gt;«θυμός велел мне» (что-то вроде «дух в груди»)&lt;/a&gt;, не «я разозлился», а «μένος наполнил меня». Боги вмешивались в их действия — не метафорически, буквально: Афина схватила Ахилла за волосы, чтобы он не убил Агамемнона.&lt;p&gt;Шумеры времён катастрофы Бронзвого века реагировали на исчезновение прямого контакта с богами как на конкретное историческое событие. Сохранилась табличка времён царя Тукульти-Нинурты: вельможа описывает, как его личный бог однажды просто перестал откликаться. Не «вера ослабла» — «бог ушёл». Это переживалось как уход родственника.&lt;p&gt;Прошло три тысячи лет. Никто сегодня не оплакивает невозможность слышать Зевса. Способность к интроспективному «я» — то, что для Гомера было либо невнятным, либо невозможным, — кажется нам настолько естественной, что мы готовы за неё умирать в тредах на Хабре.&lt;p&gt;Каждый такой переход переживается современниками как утрата человечности. Каждый раз через два-три поколения утрата становится незаметной — потому что новое поколение не знает, что было до.&lt;p&gt;Нет оснований полагать, что переход «контрсуггестивное-я → что-то-после» будет переживаться как-то существенно иначе. Болезненно — да. Катастрофически — вряд ли.&lt;h3&gt;«Но это будут уже не люди»&lt;/h3&gt;&lt;p&gt;Главное возражение к этому ходу мысли: если убрать контрсуггестивное «я», получится не новый человек, а не-человек.&lt;p&gt;Ответ в два шага — потому что у нас на руках две разные оси: джейнсовская (как человек себя описывает) и поршневская (из чего «я» собирается). Их полезно разводить.&lt;p&gt;По джейнсовской оси граница «человеческого» сдвигалась всю историю. Гомеровский грек описывал себя через органы тела и внешних богов; шумер — через ушедшее присутствие; современный человек — через ментальные пространства и внутренние состояния. Поколение Z уже частично описывает себя через распределённую медиа-сигнатуру — фаундер дейтингового стартапа Schmooze формулирует это прямо: «для поколения Z мемы — это окно во внутреннее Я». Будущий пользователь LLM-посредников — возможно, через поле «я+мои-модели». Это разные режимы самоописания, и переход от одного к другому не превращает ни предыдущего носителя в не-человека, ни следующего — в пост-человека. Меняется лексическая инфраструктура, не онтология.&lt;p&gt;По поршневской оси разговор сложнее, и здесь надо признать прямо: изменение может быть уже онтологическим. Если «я» собирается в зазоре между суггестией и контрсуггестией, и если LLM-посредничество действительно рассеивает суггестора (или прячет его на инфраструктурный уровень, недоступный индивидуальному восприятию) — меняется не словарь самоописания, а сам материал сборки. В этом смысле возражение «получится не-человек» по поршневской оси не отбрасывается, а принимается: да, получится другая конструкция.&lt;p&gt;Спор тогда не о том, изменится ли онтология, а о том, имеет ли смысл оплакивать конкретную её версию. И здесь возвращается джейнсовский аргумент: каждый носитель текущей конструкции убеждён, что именно его конструкция — настоящая, а следующая — расчеловечивающая. Это устойчивый паттерн.&lt;p&gt;Нынешнее «я» — последняя на сегодня позиция в этом ряду. Не финальная.&lt;h3&gt;Куда уходит гонка&lt;/h3&gt;&lt;p&gt;Хэппи энда рисовать не будем.&lt;p&gt;Та гонка, о которой шла речь выше, не заканчивается. Она &lt;em&gt;меняет участников&lt;/em&gt;.&lt;p&gt;Если индивидуальное «я» рассеивается под LLM-посредничеством, это не значит, что суггестивная асимметрия исчезает. Она просто перетекает на другой уровень: владелец инфраструктуры → пользовательская популяция. Тот, кто настраивает модели, получает суггестивный канал, на который у пользователя нет контрсуггестивного аппарата — потому что аппарат был заточен на людей, а не на инфраструктуру.&lt;p&gt;Дальше варианты:&lt;p&gt;&lt;strong&gt;Вариант А.&lt;/strong&gt; Контрсуггестия восстанавливается на новом уровне — но не у индивида против собеседника, а у &lt;em&gt;коллективов&lt;/em&gt; против владельцев моделей. Регуляции, открытые модели, эпистемические гигиены, общественное недоверие к посредникам. Гонка возобновляется с новыми участниками.&lt;p&gt;&lt;strong&gt;Вариант Б.&lt;/strong&gt; Не восстанавливается. Тогда гонка действительно заканчивается — победителем оказывается тот, у кого в руках инфраструктура.&lt;p&gt;Оба варианта снимают сакральность нынешней индивидуальной самости. В варианте А она оказывается промежуточной формой, уступившей место коллективным контрсуггестивным структурам. В варианте Б — последней формой, проигравшей в гонке.&lt;p&gt;И в обоих случаях оплакивать имеет смысл не «человека вообще», а конкретный исторический формат человека, у которого был свой расцвет и который, как все форматы, заканчивается.&lt;h3&gt;Финал&lt;/h3&gt;&lt;p&gt;Провокация эпохи LLM — не в самом ИИ. Она в том, что ИИ заставляет увидеть собственную субъектность не как самостоятельную сущность, а как &lt;strong&gt;позицию в коммуникативной структуре&lt;/strong&gt;. Позиция может быть занята, освобождена, переразмещена. Защищать её имеет смысл — но не как защищают святыню, а как защищают полезный, но устаревающий протокол: пока нет лучшего, и пока понимаешь, что именно ты защищаешь.&lt;p&gt;Контрсуггестивное «я» было хорошим решением для долгого исторического раунда. Оно дало вам способность не подчиняться вожаку, не верить пропаганде, держаться своей правды против чужого слова. Оно же дало вам тревогу, одиночество, статусную гонку и хроническое недоверие.&lt;p&gt;Возможно, раунд заканчивается. Возможно, начинается следующий, в котором роли распределены иначе. И «человек», которого защищают паникующие треды, окажется в нём примерно тем же, чем гомеровский герой со своим θυμός оказался для нас: предком, узнаваемым, любопытным, но больше не нами.&lt;p&gt;Если так — это не катастрофа. Это окончание раунда. Возможно, не самого удачного.&lt;p&gt;И - неиронично предлагает ИИ-шка, с которой я оформлял эту статью:&lt;pre&gt;&lt;code&gt;И провокация даже не в том, что нам не должно быть жалко.&#xA;&#xA;Провокация в том, что, возможно, не очень-то и жалко уже.&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;hr&gt;&lt;p&gt;&lt;a href=https://t.me/pcnc_pro rel=&#34;noopener noreferrer nofollow&#34;&gt;Больше историй об айти и географии у меня в тг&lt;/a&gt;&lt;hr&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>aeremenok</author>
      <guid>https://habr.com/ru/articles/1030172/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030172</guid>
      <pubDate>Thu, 30 Apr 2026 13:49:18 +0000</pubDate>
    </item>
    <item>
      <title>MAX и метка Spyware в Cloudflare: что это значит и к чему может привести</title>
      <link>https://habr.com/ru/articles/1030170/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030170</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/229/e28/7d4/229e287d4293a69443133df06c094d39.jpg width=1044 height=1280 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/229/e28/7d4/229e287d4293a69443133df06c094d39.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/229/e28/7d4/229e287d4293a69443133df06c094d39.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;30 апреля в сети появилась информация, что глобальная IT-компания и оператор крупной интернет-инфраструктуры &lt;strong&gt;Cloudflare присвоила домену национального мессенджера Max классификацию Spyware&lt;/strong&gt; (шпионское ПО). Прежде чем мысленно ухмыльнуться и подумать «а я же говорил», давайте объективно разберемся, что это за шум.&lt;p&gt;В этой статье мы расскажем: кто такая компания Cloudflare, почему она ставит подобные метки, что значит «spyware», на основании каких признаков Cloudflare принимает такие решения, а также какие последствия такая метка может иметь.&lt;h3&gt;Что такое Cloudflare&lt;/h3&gt;&lt;p&gt;Cloudflare — американская интернет-инфраструктурная компания. Она появилась в 2009 году, а публично запустилась в 2010-м. Основатели компании — Мэтью Принс, Мишель Затлин и Ли Холлоуэй. Изначальная идея проекта описывалась как &lt;strong&gt;firewall in the cloud&lt;/strong&gt;, то есть «фаервол в облаке»: система, которая помогает сайтам и приложениям защищаться от вредоносного трафика и при этом работать быстрее.&lt;p&gt;Сегодня Cloudflare — один из крупнейших игроков в интернет-инфраструктуре, обрабатывающий около &lt;strong&gt;20% всего мирового интернет-трафика&lt;/strong&gt;. Это значит, что каждый пятый запрос в интернете проходит через их оборудование. У Cloudflare сотни дата-центров в десятках стран, что позволяет быстро обслуживать пользователей из разных регионов.&lt;p&gt;Это НЕ «еще один сайт с проверкой доменов», а компания, которая видит огромный объем интернет-трафика и обслуживает большое количество сайтов, приложений, корпоративных сетей и DNS-запросов. Именно поэтому к их мнению прислушиваются государственные регуляторы и корпорации.&lt;h4&gt;Какие функции выполняет Cloudflare&lt;/h4&gt;&lt;p&gt;Если объяснять просто, Cloudflare — это сервис, который помогает сайтам и приложениям &lt;strong&gt;работать быстрее, стабильнее и безопаснее&lt;/strong&gt;.&lt;p&gt;&lt;strong&gt;Защита от DDoS-атак.&lt;/strong&gt; DDoS-атака — это ситуация, когда на сайт или приложение одновременно отправляют огромное количество запросов. Часто это делают не реальные люди, а боты. Сервер может не выдержать такой нагрузки и перестать открываться. Cloudflare принимает этот поток на себя, отсеивает подозрительные запросы и пропускает нормальных пользователей.&lt;p&gt;&lt;strong&gt;Ускорение загрузки сайтов и приложений через CDN.&lt;/strong&gt; CDN — это Content Delivery Network, или «сеть доставки контента». Проще говоря, это сеть серверов в разных странах и городах, где могут храниться копии картинок, видео, файлов, скриптов и других элементов сайта. Пользователь получает их не с одного далекого сервера, а с ближайшей точки Cloudflare. Поэтому сайт или приложение могут открываться быстрее.&lt;p&gt;&lt;strong&gt;Публичный DNS-сервис 1.1.1.1.&lt;/strong&gt; DNS — это «адресная книга интернета». Когда пользователь вводит адрес сайта, например example.ru, DNS помогает устройству понять, к какому серверу нужно обратиться. Cloudflare предоставляет свой публичный DNS-сервис 1.1.1.1, который используется для быстрого и относительно приватного поиска таких адресов.&lt;p&gt;&lt;strong&gt;Web Application Firewall, или WAF.&lt;/strong&gt; Firewall — это «сетевой экран», то есть защитный фильтр. А Web Application Firewall — это фильтр именно для сайтов, приложений и API. Он проверяет HTTP-запросы. HTTP-запрос — это обычный запрос браузера или приложения к серверу: открыть страницу, получить профиль, отправить форму, загрузить сообщение. Например, нормальный запрос может выглядеть так:&lt;p&gt;🔹 GET /profile?id=123&lt;p&gt;А подозрительный — так:&lt;p&gt;🔹 GET /profile?id=1%20OR%201=1--&lt;p&gt;Второй вариант похож на попытку SQL-инъекции: злоумышленник передает в параметре URL не обычный идентификатор профиля, а фрагмент SQL-условия. Если приложение небезопасно подставляет этот параметр в SQL-запрос, такая конструкция может изменить логику обращения к базе данных. WAF может распознать характерный паттерн атаки и заблокировать запрос еще до того, как он попадёт на сервер приложения.&lt;p&gt;&lt;strong&gt;Bot Protection — защита от ботов.&lt;/strong&gt; Боты могут автоматически перебирать пароли, массово создавать аккаунты, парсить цены, копировать контент, отправлять спам или перегружать приложение запросами. Cloudflare анализирует такое поведение и может ограничивать, замедлять или блокировать подозрительную активность.&lt;p&gt;&lt;strong&gt;Фильтрация вредоносного трафика.&lt;/strong&gt; Cloudflare может отсекать запросы, которые похожи на атаки, сканирование уязвимостей, фишинг, вредоносные скрипты или попытки получить несанкционированный доступ. Для обычного пользователя это незаметно: он просто открывает сайт, а подозрительный трафик до сервера не доходит.&lt;p&gt;&lt;strong&gt;Корпоративные решения безопасности, включая Zero Trust.&lt;/strong&gt; Zero Trust — это подход «никому не доверять автоматически». Даже если сотрудник находится внутри корпоративной сети, его доступ к сервисам все равно проверяется: кто он, с какого устройства заходит, имеет ли право открыть конкретный ресурс. У Cloudflare есть такие инструменты для компаний: они помогают защищать сотрудников, внутренние системы, рабочие приложения и корпоративный интернет-трафик.&lt;h3&gt;Что такое Cloudflare Gateway&lt;/h3&gt;&lt;p&gt;Gateway — это корпоративный фильтр интернета от Cloudflare. Он стоит не перед сайтом, а перед пользователем компании. Например, сотрудник открывает сайт или приложение, а Gateway проверяет: можно ли туда идти?&lt;p&gt;🔹Сотрудник → Cloudflare Gateway → сайт / приложение&lt;p&gt;&lt;strong&gt;Компания может настроить правила:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;блокировать malware: сайты, которые пытаются заразить ваш компьютер вирусом.&lt;li&gt;&lt;p&gt;блокировать phishing: поддельные страницы, крадущие пароли.&lt;li&gt;&lt;p&gt;блокировать spyware: программы, которые скрытно собирают данные пользователя (геолокацию, список контактов, данные о других приложениях) и передают их без явного согласия.&lt;li&gt;&lt;p&gt;разрешать рабочие сервисы.&lt;li&gt;&lt;p&gt;логировать (записывать в &amp;#34;дневник&amp;#34;) подозрительные обращения.&lt;/ul&gt;&lt;p&gt;Если сотрудник открывает домен, который Cloudflare относит к категории Spyware, Gateway может заблокировать доступ или показать предупреждение.&lt;p&gt;Именно для таких продуктов Cloudflare и ведет классификацию доменов. Ей нужен список: какие домены относятся к мессенджерам, какие — к соцсетям, какие — к рекламе, какие — к фишингу, какие — к вредоносному ПО.&lt;h3&gt;Что за реестр ведет Cloudflare&lt;/h3&gt;&lt;p&gt;Cloudflare ведет собственную систему категоризации доменов — Domain Categorization. Посмотреть категории конкретного домена можно через &lt;strong&gt;Cloudflare Radar &lt;/strong&gt;(https://radar.cloudflare.com/ru-ru/domains/domain/example.ru)&lt;p&gt;&lt;strong&gt;Категории делятся на два больших типа:&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Content categories&lt;/strong&gt; — это обычные тематические категории. Например: мессенджеры, соцсети, новости, реклама, развлечения, игры, финансы, образование, взрослый контент.&lt;p&gt;&lt;strong&gt;Security categories&lt;/strong&gt; — это категории, связанные с рисками и угрозами. Например:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Malware&lt;/strong&gt; — сайты, связанные с вредоносным ПО;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Phishing&lt;/strong&gt; — фишинговые домены, которые могут красть логины, пароли или платёжные данные;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Spyware&lt;/strong&gt; — домены, связанные со шпионским ПО;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Command and Control / Botnet&lt;/strong&gt; — домены, к которым обращаются зараженные устройства;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Compromised Domain&lt;/strong&gt; — легитимный домен, который был взломан и использован для вредоносной активности;&lt;li&gt;&lt;p&gt;&lt;strong&gt;DNS Tunneling&lt;/strong&gt; — подозрительная передача данных через DNS-запросы.&lt;/ul&gt;&lt;p&gt;Домены категоризирует &lt;strong&gt;Cloudforce One&lt;/strong&gt; — threat intelligence-подразделение Cloudflare, то есть команда/платформа по анализу киберугроз.&lt;h3&gt;Что такое Spyware&lt;/h3&gt;&lt;p&gt;Spyware — это шпионское программное обеспечение. В широком смысле так называют программы или компоненты, которые могут собирать информацию о пользователе без достаточной прозрачности или без понятного согласия.&lt;p&gt;&lt;strong&gt;Это может быть:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Сбор данных об устройстве&lt;/strong&gt; — например, информации о модели телефона, операционной системе, настройках сети, IP-адресе, идентификаторах устройства или других технических параметрах.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Отслеживание активности пользователя&lt;/strong&gt; — если приложение или связанные с ним домены передают данные о действиях пользователя чаще или шире, чем это необходимо для работы сервиса.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Передача телеметрии на сторонние серверы&lt;/strong&gt; — телеметрия сама по себе не всегда опасна: многие приложения собирают технические данные о сбоях и производительности. Вопросы возникают, если такая передача непрозрачна, чрезмерна или идет на подозрительные домены.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Доступ к чувствительной информации&lt;/strong&gt; — например, к контактам, файлам, геолокации, данным об устройстве или другим сведениям, которые неочевидно нужны для основной функции приложения.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Скрытая сетевая активность&lt;/strong&gt; — если через DNS-запросы или корпоративные фильтры вроде Gateway фиксируются обращения к подозрительным серверам, не связанным напрямую с работой мессенджера, например с чатами, звонками или авторизацией.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Аномальные DNS-запросы&lt;/strong&gt; — DNS — это служба, которая переводит понятные адреса сайтов в IP-адреса серверов. Подозрения могут возникать, если приложение регулярно обращается к странным, недавно созданным или уже замеченным в угрозах доменам.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Поведение, похожее на наблюдение или слежку&lt;/strong&gt; — например, когда приложение собирает больше данных, чем ожидает пользователь, делает это без понятного объяснения или передает информацию так, что ее назначение трудно проверить.&lt;/ul&gt;&lt;p&gt;Но важно: когда Cloudflare ставит домену категорию Spyware, это не всегда означает, что все приложение уже доказано является шпионским.&lt;h3&gt;По каким признакам Cloudflare может поставить такую метку&lt;/h3&gt;&lt;p&gt;Cloudflare не раскрывает полностью все алгоритмы классификации. Это нормально для компаний кибербезопасности: если подробно раскрыть правила, злоумышленники смогут их обходить.&lt;p&gt;Но из документации Cloudflare понятно, что при оценке доменов могут использоваться разные источники и сигналы: внутренние данные, машинное обучение, коммерческие базы угроз, открытые данные по киберугрозам, репутация домена, возраст домена, связь с подозрительной инфраструктурой.&lt;p&gt;Простыми словами, подозрительными могут выглядеть такие вещи:&lt;ul&gt;&lt;li&gt;&lt;p&gt;домен связан с вредоносными файлами или скриптами;&lt;li&gt;&lt;p&gt;домен участвует в подозрительной передаче данных;&lt;li&gt;&lt;p&gt;домен фигурирует в базах киберугроз;&lt;li&gt;&lt;p&gt;домен связан с инфраструктурой, которая уже замечена в атаках;&lt;li&gt;&lt;p&gt;приложение или сайт обращается к подозрительным сторонним доменам;&lt;li&gt;&lt;p&gt;поведение запросов похоже на сбор данных, командный сервер или скрытую телеметрию;&lt;li&gt;&lt;p&gt;домен получает негативные сигналы из нескольких независимых источников.&lt;/ul&gt;&lt;p&gt;Но это не обязательно значит, что все именно так и произошло в случае MAX. В публикациях по MAX уже приводится позиция пресс-службы мессенджера: там говорят об ошибочной классификации и неверной интерпретации запросов к веб-аналитике.&lt;p&gt;То есть сейчас есть сам факт метки Cloudflare и есть объяснение со стороны сервиса. Окончательный технический вывод без независимого аудита делать рано.&lt;h3&gt;Что может произойти с MAX после такой метки&lt;/h3&gt;&lt;p&gt;Сама по себе метка Cloudflare не означает автоматического удаления приложения из App Store или Google Play. Она также не означает, что &lt;strong&gt;TLS-сертификат&lt;/strong&gt; — цифровое удостоверение домена, которое подтверждает подлинность сайта или сервера и позволяет устанавливать защищенное HTTPS-соединение, — будет обязательно отозван.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Во-первых, домен могут начать блокировать корпоративные сети. Если компания использует Cloudflare Gateway и настроила правило «блокировать Spyware», доступ к max.ru может быть ограничен.&lt;li&gt;&lt;p&gt;Во-вторых, у сервиса может появиться репутационный риск. Для пользователей и ИБ-специалистов метка от Cloudflare — это повод внимательнее посмотреть на приложение, его домены, сертификаты, сетевые запросы и политику обработки данных.&lt;li&gt;&lt;p&gt;В-третьих, возможны дополнительные проверки со стороны магазинов приложений. Apple и Google могут проверять приложения на соответствие требованиям безопасности и приватности. Apple прямо пишет, что приложения проходят review, а App Store должен оставаться безопасным местом для пользователей.&lt;li&gt;&lt;p&gt;В-четвертых, внимание могут обратить удостоверяющие центры, которые выдают TLS-сертификаты. Если они увидят основания для проверки, они могут запросить объяснения или провести собственную оценку.&lt;li&gt;&lt;p&gt;В-пятых, вокруг сервиса может усилиться внимание со стороны исследователей безопасности, журналистов, регуляторов и пользователей (&lt;s&gt;хотя, казалось бы, куда уж больше&lt;/s&gt;).&lt;/ol&gt;&lt;h3&gt;Пример Telega: почему историю с MAX начали сравнивать именно с ней&lt;/h3&gt;&lt;p&gt;Параллель с &lt;strong&gt;Telega &lt;/strong&gt;возникла не случайно. Telega — альтернативный клиент Telegram — ранее тоже получил метку Spyware у Cloudflare. Речь шла о рабочих доменах проекта, в том числе telega.me и api.telega.info. По сообщениям СМИ, это произошло 9 апреля 2026 года. В тот же день Telega исчезла из App Store, при этом в Google Play и RuStore приложение оставалось доступно.&lt;p&gt;По данным Meduza со ссылкой на «Осторожно, новости», после метки Cloudflare у Telega также был отозван TLS-сертификат — цифровое удостоверение домена, которое нужно для защищенного HTTPS-соединения. Предположительно, именно это могло стать одним из факторов удаления приложения из App Store.&lt;p&gt;Через несколько дней разработчики Telega заявили, что Cloudflare сняла классификацию Spyware с доменов проекта, включая telega.me, api.telega.info и telega.info. Однако возвращения в App Store это сразу не обеспечило. Отдельный этап начался 17 апреля 2026 года: пользователи iPhone начали сообщать, что iOS помечает уже установленную Telega как потенциально вредоносное приложение, блокирует запуск и предлагает удалить его.&lt;p&gt;&lt;strong&gt;Эта история важна как пример возможной цепочки:&lt;/strong&gt;&lt;p&gt;🔹Метка Cloudflare → внимание к доменам → проблемы с сертификатами → вопросы со стороны Apple → удаление или блокировка приложения&lt;p&gt;Но это не значит, что с MAX обязательно произойдет то же самое. У MAX, по сообщениям СМИ на момент публикации, есть действующий TLS-сертификат, а приложение на данный момент доступно в App Store и Google Play.&lt;h3&gt;Что все это значит для пользователей&lt;/h3&gt;&lt;p&gt;Главное — не путать три разные вещи.&lt;p&gt;Первое: Cloudflare Radar действительно может показывать домен в категории Spyware. Это серьезный сигнал, потому что Cloudflare — крупный игрок в интернет-безопасности.&lt;p&gt;Второе: такая метка не является юридическим решением и не доказывает автоматически, что приложение шпионит за пользователями.&lt;p&gt;Третье: даже если это не доказательство, это повод для дополнительных вопросов. Особенно когда речь идет о мессенджере — сервисе, которому пользователи доверяют переписку, контакты, звонки, файлы и иногда рабочие коммуникации.&lt;h3&gt;Заключение&lt;/h3&gt;&lt;p&gt;История с MAX и Cloudflare — это не приговор, но и не мелочь, которую можно полностью игнорировать.&lt;p&gt;Cloudflare — крупная и влиятельная инфраструктурная компания. Ее сеть работает в сотнях городов, защищает значительную часть веба и используется многими компаниями для фильтрации угроз. Поэтому метка Spyware в Cloudflare Radar — это заметный репутационный и технический сигнал.&lt;p&gt;Но важно сохранять точность. Cloudflare не вынесла судебное решение, не запретила MAX и не доказала публично, что приложение является шпионским ПО. Она классифицировала домен max.ru в своей системе как Spyware. Это может привести к блокировкам в корпоративных сетях, дополнительным проверкам, вопросам к сертификатам и вниманию со стороны магазинов приложений. Но само по себе это еще не означает автоматического удаления приложения или официального признания его вредоносным.&lt;p&gt;Самая корректная позиция сейчас такая: &lt;strong&gt;ситуация требует внимательного наблюдения и технических пояснений, но резкие выводы делать рано&lt;/strong&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>DevRoad</author>
      <guid>https://habr.com/ru/articles/1030170/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030170</guid>
      <pubDate>Thu, 30 Apr 2026 13:48:26 +0000</pubDate>
    </item>
    <item>
      <title>EIP-7702: прикручиваем код к EOA, где можно споткнуться?</title>
      <link>https://habr.com/ru/articles/1029386/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029386</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;&lt;strong&gt;EIP-7702: Set Code for EOAs&lt;/strong&gt; - это стандарт, который предлагает добавить &lt;strong&gt;новый тип&lt;/strong&gt; транзакции согласно спецификации, описанной в &lt;a href=https://eips.ethereum.org/EIPS/eip-2718 rel=&#34;noopener noreferrer nofollow&#34;&gt;EIP-2718: Typed Transaction Envelope&lt;/a&gt;, что позволит к Externally Owned Accounts (EOAs) прикрепить код смарт-контракта.&lt;p&gt;EIP-7702 является следующим шагом в области абстракции аккаунта в рамках обновления Ethereum под названием &lt;a href=https://ethereum.org/en/roadmap/pectra/ rel=&#34;noopener noreferrer nofollow&#34;&gt;&amp;#34;Pectra&amp;#34;&lt;/a&gt;.&lt;p&gt;Прикрепление смарт-контракта к EOA позволяет исполнить программный код в его контексте. Например, использовать баланс пользователя для отправки другому пользователю. Технически это реализовано при помощи механизма делегирования вызова (delegateCall), только не для смарт-контракта, а для EOA.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/b8d/c1b/fbb/b8dc1bfbb8e5c1d982d11c068bea4ac8.png alt=&#34;Делегирование вызова для EOA cо смарт-контрактом&#34; title=&#34;Делегирование вызова для EOA cо смарт-контрактом&#34; width=1390 height=538 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/b8d/c1b/fbb/b8dc1bfbb8e5c1d982d11c068bea4ac8.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/b8d/c1b/fbb/b8dc1bfbb8e5c1d982d11c068bea4ac8.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Делегирование вызова для EOA cо смарт-контрактом&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;У сущности аккаунт в Ethereum всегда существовало поле &lt;strong&gt;code&lt;/strong&gt;. Только раньше, для EOA, это поле оставалось пустым, а для смарт-контракта там находился байткод.&lt;p&gt;Теперь для EOA, в поле &lt;strong&gt;code&lt;/strong&gt;, записывается адрес прикрепляемого смарт-контракта со специальным префиксом (&lt;strong&gt;0xef0100&lt;/strong&gt; || address). По сути, префикс - это некоторое магическое значение, которое позволяет четко определить, что это адрес для делегирования, а не байт-код смарт-контракта.&lt;p&gt;Глобально, EIP-7702 нацелен на улучшение UX приложений за счет небольших изменений внутри Ethereum. При помощи стандарта могут решаться следующие задачи:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Батчинг транзакций&lt;/strong&gt;: Объединение нескольких атомарных операций внутри одной транзакции. Например, approve и трансфер в одной транзакции. Это то чего так долго ждали пользователи кошельков.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Безгазовые транзакции или спонсирование&lt;/strong&gt;: Оплата газа сторонним аккаунтом или возможность оплачивать газ в ERC-20 токене. Это позволяет улучшить опыт использования кошельков новыми пользователями, у которых еще нет нативной валюты для оплаты газа.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Управление ролями и доступом&lt;/strong&gt;: Выдача разрешений на управление аккаунтом третьим лицам.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Механизмы восстановления доступа&lt;/strong&gt;: Может быть реализован вывод активов или резервный адрес, который сможет управлять активами кошелька.&lt;li&gt;&lt;p&gt;&lt;strong&gt;и так далее&lt;/strong&gt;.&lt;/ul&gt;&lt;h3&gt;Транзакция для прикрепления кода&lt;/h3&gt;&lt;p&gt;В этом разделе разберем из чего состоит транзакция, которая прикрепит смарт-контракт к EOA.&lt;p&gt;Тело транзакции похоже на любой другой тип транзакции согласно &lt;a href=https://eips.ethereum.org/EIPS/eip-2718 rel=&#34;noopener noreferrer nofollow&#34;&gt;EIP-2718: Typed Transaction Envelope&lt;/a&gt;:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;TransactionType&lt;/strong&gt; - тип транзакции равен &lt;code&gt;0x04&lt;/code&gt;.&lt;li&gt;&lt;p&gt;&lt;strong&gt;TransactionPayload&lt;/strong&gt; - кодированные данные отличаются тем, что добавляется новое поле &lt;code&gt;authorization_list&lt;/code&gt;.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/bcf/bc5/de3/bcfbc5de39a6fb66b8c90fe1ef9fc5f6.png alt=&#34;Структура транзакции для прикрепления смарт-контракта&#34; title=&#34;Структура транзакции для прикрепления смарт-контракта&#34; width=1096 height=1360 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/bcf/bc5/de3/bcfbc5de39a6fb66b8c90fe1ef9fc5f6.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/bcf/bc5/de3/bcfbc5de39a6fb66b8c90fe1ef9fc5f6.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Структура транзакции для прикрепления смарт-контракта&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Поле &lt;code&gt;authorization_list&lt;/code&gt; - это tuple, который определяется как:&lt;pre&gt;&lt;code class=javascript&gt;authorization_list = [[&#xA;  chain_id, // Идентификатор сети, для которой делегирование актуально&#xA;  address, // Адрес смарт-контракта куда будет делегироваться вызов&#xA;  nonce, // Текущий nonce EOA&#xA;  y_parity, // Данные подписи EOA аккаунта&#xA;  r, // Данные подписи EOA аккаунта&#xA;  s // Данные подписи EOA аккаунта&#xA;], ...]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Как и любую другую транзакцию пользователь может подписать ее, а послана транзакция в сеть может быть позже.&lt;p&gt;Поэтому тут важно понимать, что если &lt;code&gt;chain_id&lt;/code&gt; в &lt;code&gt;authorization_list&lt;/code&gt; передать равным 0, то это будет означать, что EOA разрешает использовать транзакцию во всех EVM-совместимых сетях, поддерживающих EIP-7702.&lt;p&gt;&lt;strong&gt;Интересные моменты&lt;/strong&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Для того, чтобы подпись, разрешающая прикрепление EOA, была валидна в нескольких сетях, необходимо, чтобы &lt;code&gt;nonce&lt;/code&gt; был одинаковым в этих сетях, иначе подпись будет невалидна для какой-то из сетей. Это может быть удобно, если создается новый аккаунт, владелец дает всего одну подпись для того, чтобы привязать код к EOA сразу в нескольких сетях.&lt;li&gt;&lt;p&gt;Поле &lt;code&gt;authorization_list&lt;/code&gt; в транзакции может содержит список данных, подписанных несколькими разными EOA. При этом инициатором транзакции может быть третья сторона, что позволяет реализовать безгазовое прикрепление кода смарт-контракта к нескольким EOA.&lt;li&gt;&lt;p&gt;Для того, чтобы снова сделать свой EOA обычным (открепить смарт-контракт) достаточно установить поле &lt;code&gt;address&lt;/code&gt; в &lt;code&gt;authorization_list&lt;/code&gt; в значение 0.&lt;li&gt;&lt;p&gt;Если один из элементов списка &lt;code&gt;authorization_list&lt;/code&gt; невалидный, то он пропускается, валидные элементы списка применяются, транзакция не откатывается.&lt;/ol&gt;&lt;h3&gt;Как это работает для пользователя&lt;/h3&gt;&lt;p&gt;Пользователь подписывает сообщение для добавления кода к своему аккаунту, включающее: идентификатор цепочки, одноразовый код, адрес делегирования. Больше от него ничего не зависит.&lt;p&gt;Эта сообщение может быть доставлено в сеть с транзакцией и после этого делегирование будет работать.&lt;p&gt;Технически EOA никуда не исчезает и с ним ничего не происходит. Средства пользователя не перемещаются, и пользователь по-прежнему управляет закрытым ключам аккаунта. Поэтому все также нужно следить за его сохранностью и не позволять его компрометировать.&lt;p&gt;Прикрепление кода к аккаунту позволит пользователю получить больше функций.&lt;p&gt;Встретить подобное можно уже сейчас, например, Metamask встроил EIP-7702 в свою фичу &amp;#34;&lt;a href=https://support.metamask.io/configure/accounts/what-is-a-smart-account/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Smart Account&lt;/a&gt;&amp;#34;. Для этого Metamask разработал свои собственные смарт-контракты, то есть пользователь переводя свой EOA на Smart Account, будет делегировать выполнение смарт-контрактам от Metamask.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/28b/56d/9a2/28b56d9a20c86c7a4be144a6b71e0894.png alt=&#34;Metamask предлагает умные аккаунты&#34; title=&#34;Metamask предлагает умные аккаунты&#34; width=708 height=1116 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/28b/56d/9a2/28b56d9a20c86c7a4be144a6b71e0894.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/28b/56d/9a2/28b56d9a20c86c7a4be144a6b71e0894.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Metamask предлагает умные аккаунты&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ab0/6d7/25f/ab06d725f2b5ff0d7afbc989837b58e1.png alt=&#34;Metamask: подтверждение перехода на умный аккаунт&#34; title=&#34;Metamask: подтверждение перехода на умный аккаунт&#34; width=772 height=1176 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ab0/6d7/25f/ab06d725f2b5ff0d7afbc989837b58e1.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ab0/6d7/25f/ab06d725f2b5ff0d7afbc989837b58e1.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Metamask: подтверждение перехода на умный аккаунт&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h3&gt;Неочевидные моменты для смарт-контрактов&lt;/h3&gt;&lt;p&gt;В этом разделе разберем нюансы работы EIP-7702. Рабочие примеры будем писать при помощи Foundry, который для тестирования реализует необходимые Cheatcodes.&lt;pre&gt;&lt;code class=javascript&gt;function signDelegation(address implementation, uint256 privateKey)&#xA;    external&#xA;    returns (SignedDelegation memory signedDelegation);&#xA;&#xA;function attachDelegation(SignedDelegation calldata signedDelegation) external;&#xA;&#xA;function signAndAttachDelegation(address implementation, uint256 privateKey)&#xA;    external&#xA;    returns (SignedDelegation memory signedDelegation);&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Эти Cheatcodes позволяют подписывать транзакцию по прикреплению кода к EOA (signDelegation) и отправлять транзакции в сеть (attachDelegation) прямо в тестах.&lt;p&gt;Для того, чтобы EIP-7702 работал, необходимо убедиться, что компилируется код для evm версии не ниже &amp;#34;prague&amp;#34;. Это может быть определено в &lt;code&gt;foundry.toml&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// foundry.toml&#xA;evm_version = &amp;#34;prague&amp;#34;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Таким образом, самый простой пример, который прикрепит код к EOA будет выглядеть в тестах Foundry следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;// Объявляем StdCheats.Account public user;&#xA;...&#xA;&#xA;function test_attachCode() external {&#xA;    // Проверяем наличие кода у EOA user&#xA;    console.logBytes(user.addr.code); // 0x&#xA;&#xA;    // Симулируем подписание транзакции на прикрепление кода пользователем&#xA;    Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(delegation), user.key);&#xA;&#xA;    vm.startBroadcast(operator.key);&#xA;&#xA;    // Отправляем транзакцию на прикрепление кода к user. Обратить внимание, что это делает operator, а не user&#xA;    vm.attachDelegation(signedDelegation);&#xA;&#xA;    vm.stopBroadcast();&#xA;&#xA;    console.logBytes(user.addr.code); //0xef01005615deb798bb3e4dfa0139dfa1b3d433cc23b72f&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;По сути, тест реализует всего два этапа для прикрепления кода.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/4be/c77/d90/4bec77d908ea285a98a1f61d0f7734b7.png alt=&#34;Шаги по прикреплению кода в тестах Foundry&#34; title=&#34;Шаги по прикреплению кода в тестах Foundry&#34; width=742 height=368 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/4be/c77/d90/4bec77d908ea285a98a1f61d0f7734b7.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/4be/c77/d90/4bec77d908ea285a98a1f61d0f7734b7.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Шаги по прикреплению кода в тестах Foundry&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Полный пример теста &lt;code&gt;test_attachCode&lt;/code&gt; в &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/attachCode/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.t.sol&lt;/a&gt;.&lt;h4&gt;Хранилище делегированного смарт-контракта&lt;/h4&gt;&lt;p&gt;Хранилище делегированного смарт-контракта, не является доступным для вызовов через EOA.&lt;p&gt;Более того, конструктор такого смарт-контракта не вызвать в контексте EOA, поэтому нельзя устанавливать данные через конструктор в хранилище в контексте EOA.&lt;p&gt;Изменение переменных хранилища при вызове остается в контексте EOA. То есть хранилище самого смарт-контракта не будет изменено.&lt;p&gt;Для проверки работы хранилища реализуем простой смарт-контракт &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/storageExample/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.sol&lt;/a&gt;.&lt;pre&gt;&lt;code class=javascript&gt;contract Delegation {&#xA;    uint256 private _value;&#xA;&#xA;    constructor(uint256 initialValue) {&#xA;        _value = initialValue;&#xA;    }&#xA;&#xA;    function setValue(uint256 newValue) external {&#xA;        _value = newValue;&#xA;    }&#xA;&#xA;    function getValue() external view returns (uint256) {&#xA;        return _value;&#xA;    }&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/storageExample/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;тесте&lt;/a&gt; будем работать с хранилищем смарт-контракта напрямую и в контексте EOA:&lt;pre&gt;&lt;code class=javascript&gt;// Delegation.t.sol&#xA;function test_workWithStorage(uint256 value) external {&#xA;    // Прикрепляем код смарт-контракта Delegation к EOA user&#xA;    ...&#xA;&#xA;    // Step 1. Проверяем, что хранилище user пустое, а смарт-контракта delegation нет&#xA;&#xA;    // Вызов getValue через user вернет 0, так как хранилище user пустое&#xA;    assertEq(Delegation(user.addr).getValue(), 0);&#xA;    // Вызов getValue через delegation вернет _INITIAL_VALUE, так как хранилище delegation не изменилось и было установлено через конструктор при деплое&#xA;    assertEq(delegation.getValue(), _INITIAL_VALUE);&#xA;&#xA;    // Step 2. Устанавливаем значение в хранилище user&#xA;    Delegation(user.addr).setValue(value);&#xA;&#xA;    // Вызов getValue через user вернет установленное value&#xA;    assertEq(Delegation(user.addr).getValue(), value);&#xA;    // Вызов getValue через delegation вернет _INITIAL_VALUE, хранилище не изменилось&#xA;    assertEq(delegation.getValue(), _INITIAL_VALUE);&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Тест показывает, что конструктор смарт-контракта Delegation ничего не установил в хранилище EOA. В целом хранилище EOA и самого смарт-контракта - это разные хранилища.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/2e4/bca/133/2e4bca133f8c0cbb1cead4d657db56b6.png alt=&#34;Работа хранилища смарт-контракта и EOA&#34; title=&#34;Работа хранилища смарт-контракта и EOA&#34; width=1076 height=1202 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/2e4/bca/133/2e4bca133f8c0cbb1cead4d657db56b6.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/2e4/bca/133/2e4bca133f8c0cbb1cead4d657db56b6.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Работа хранилища смарт-контракта и EOA&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Полный пример кода теста &lt;code&gt;test_workWithStorage&lt;/code&gt; в &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/storageExample/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.t.sol&lt;/a&gt;.&lt;p&gt;&lt;em&gt;Важно!&lt;/em&gt; На самом деле конструктор смарт-контракта все еще может применяться для immutable переменных, так как такие переменные будут частью байткода смарт-контракта после деплоя смарт-контракта.&lt;pre&gt;&lt;code class=javascript&gt;contract Delegation {&#xA;    uint256 immutable private _value;&#xA;&#xA;    constructor(uint256 value) {&#xA;        _value = value;&#xA;    }&#xA;&#xA;    function getValue() external view returns (uint256) {&#xA;        return _value;&#xA;    }&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Проверочный тест может выглядеть следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;function test_workWithStorage(uint256 initialValue) external {&#xA;    StdCheats.Account memory user = makeAccount(&amp;#34;User&amp;#34;);&#xA;    StdCheats.Account memory operator = makeAccount(&amp;#34;Operator&amp;#34;);&#xA;&#xA;    // Деплоим смарт-контракт Delegation&#xA;    Delegation delegation = new Delegation(initialValue);&#xA;&#xA;    // Прикрепляем код смарт-контракта Delegation к пользователю&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.signAndAttachDelegation(address(delegation), user.key);&#xA;    vm.stopBroadcast();&#xA;&#xA;    // Проверяем, что хранилище user не пустое&#xA;    assertEq(Delegation(user.addr).getValue(), initialValue);&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;Проверки типа msg.sender == tx.origin&lt;/h4&gt;&lt;p&gt;Раньше смарт-контракты использовали условие &lt;code&gt;tx.origin == msg.sender&lt;/code&gt; для двух вещей:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Проверить, что код выполняется от имени EOA, так как &lt;code&gt;tx.origin&lt;/code&gt; не может быть смарт-контрактом.&lt;li&gt;&lt;p&gt;Защитить от повторного входа. Строится на предположение, что второй вход обязательно выполняется смарт-контрактом.&lt;/ol&gt;&lt;p&gt;&lt;em&gt;Важно!&lt;/em&gt; Если делегированный смарт-контракт в контексте EOA делает вызов к другому смарт-контракту, то &lt;code&gt;msg.sender&lt;/code&gt; равняется адресу EOA. И здесь пока &lt;code&gt;tx.origin&lt;/code&gt; равняется &lt;code&gt;msg.sender&lt;/code&gt;.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e41/601/79e/e4160179ecc6f30b02e9f69540ca9a5c.png alt=&#34;Кто sender на каждом этапе вызова?&#34; title=&#34;Кто sender на каждом этапе вызова?&#34; width=1512 height=314 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e41/601/79e/e4160179ecc6f30b02e9f69540ca9a5c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e41/601/79e/e4160179ecc6f30b02e9f69540ca9a5c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Кто sender на каждом этапе вызова?&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;На схеме изображен поток вызова от пользователя к смарт-контракту &lt;code&gt;Target&lt;/code&gt; через прикрепленный смарт-контракт &lt;code&gt;Delegation&lt;/code&gt;. Пользователь подписывает своим приватным ключом вызов на свой же адрес, выполнение делегируется к смарт- контракту &lt;code&gt;Delegation&lt;/code&gt;, который вызывает смарт-контракт &lt;code&gt;Target&lt;/code&gt;. Во всех случаях адрес пользователя равен &lt;code&gt;msg.sender&lt;/code&gt; и &lt;code&gt;tx.origin&lt;/code&gt;.&lt;p&gt;Но теперь, как обойти условие, чтобы &lt;code&gt;tx.origin&lt;/code&gt; изменился. EIP-7702 позволяет сделать любую реализацию смарт-контракта. Это означает, что вызывать его может любой другой аккаунт, если не реализовано иное. Таким образом, здесь &lt;code&gt;msg.sender&lt;/code&gt; не будет совпадать с &lt;code&gt;tx.origin&lt;/code&gt;, если первоначальный вызов будет исходить от стороннего EOA.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c00/a22/fc4/c00a22fc456c9b8c358cf8fd15d534a6.png alt=&#34;Кто sender на каждом этапе вызова?&#34; title=&#34;Кто sender на каждом этапе вызова?&#34; width=1570 height=300 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c00/a22/fc4/c00a22fc456c9b8c358cf8fd15d534a6.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c00/a22/fc4/c00a22fc456c9b8c358cf8fd15d534a6.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Кто sender на каждом этапе вызова?&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;На схеме, оператор подписывает транзакцию своим приватным ключом, который не совпадает с адресом пользователя. По итогу &lt;code&gt;tx.origin&lt;/code&gt; не будет равен &lt;code&gt;msg.sender&lt;/code&gt;.&lt;p&gt;Проверить это легко. Понадобится смарт-контракт &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/condition/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Target&lt;/a&gt;, который будет позволять устанавливать value только если &lt;code&gt;tx.origin != msg.sender&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// Target&#xA;function setValue(uint256 newValue) external {&#xA;    if (tx.origin == msg.sender) {&#xA;        revert EOACallIsNotAllowed();&#xA;    }&#xA;&#xA;    _value = newValue;&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Прикреплять к EOA будем смарт-контракт &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/condition/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation&lt;/a&gt;, который будет делать вызов &lt;code&gt;setValue()&lt;/code&gt; на смарт-контракте &lt;code&gt;Target&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;contract Delegation {&#xA;    function setValue(address target, uint256 value) external {&#xA;        Target(target).setValue(value);&#xA;    }&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Проверочный тест будет выглядеть следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;function test_checkCondition(uint256 value) external {&#xA;    Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(delegation), user.key);&#xA;&#xA;    // Симулируем, что пользователь напрямую вызывает целевой контракт и транзакция ревертится&#xA;    vm.expectRevert(Target.EOACallIsNotAllowed.selector);&#xA;    vm.prank(user.addr, user.addr);&#xA;    target.setValue(value);&#xA;&#xA;    // Operator прикрепляет смарт-контракт Delegation к user&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.attachDelegation(signedDelegation);&#xA;    vm.stopBroadcast();&#xA;&#xA;    // Operator вызывает функцию setValue() на контракте Delegation от имени user,&#xA;    // которая установит value на смарт-контракте Target&#xA;    vm.prank(operator.addr, operator.addr);&#xA;    Delegation(user.addr).setValue(address(target), value);&#xA;&#xA;    // Проверяем, что значение установлено (проверка обойдена)&#xA;    assertEq(target.getValue(), value);&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;По итогу тест показывает, что вызывать смарт-контракт, прикрепленный к EOA может другой EOA, а соответственно в &lt;code&gt;tx.origin&lt;/code&gt; будет другой адрес.&lt;p&gt;Полный пример теста &lt;code&gt;test_checkCondition&lt;/code&gt; в &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/condition/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.t.sol&lt;/a&gt;.&lt;h4&gt;Проверка адреса на то, что он смарт-контракт&lt;/h4&gt;&lt;p&gt;Больше нельзя полагаться на проверку &lt;code&gt;address(contract).code &amp;gt; 0&lt;/code&gt;, которая определяет, что адрес является смарт-контрактом и не является EOA. Теперь EOA с прикрепленным смарт-контрактом тоже возвращает значение больше 0.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e7d/79a/3f6/e7d79a3f61512a97baada24b551972ae.png alt=&#34;Проблемы при определении msg.sender&#34; title=&#34;Проблемы при определении msg.sender&#34; width=1204 height=638 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e7d/79a/3f6/e7d79a3f61512a97baada24b551972ae.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e7d/79a/3f6/e7d79a3f61512a97baada24b551972ae.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Проблемы при определении msg.sender&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Согласно схеме, пользователь, который прикрепил к своему адресу смарт-контракт, при вызове других смарт-контрактов (когда он msg.sender) будет определяться, как аккаунт с кодом.&lt;p&gt;Проверить, что &lt;code&gt;msg.sender&lt;/code&gt; - это прикрепленный смарт-контракт к аккаунту пользователя можно следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;assembly {&#xA;    // Загружаем код msg.sender&#xA;    let ptr := mload(0x40)&#xA;    extcodecopy(caller(), ptr, 0, 32)&#xA;    let prefix := shr(232, mload(ptr))&#xA;&#xA;    // Проверяем, что prefix == 0xef0100&#xA;    isDelegated := eq(prefix, 0xef0100)&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Для этого нужно получить специальный префикс, который является магическим числом &lt;code&gt;0xef0100&lt;/code&gt; и всегда будет в начале байткода делегированного смарт-контракта.&lt;h4&gt;Получение любого вида токена, который требует наличие callback функции, если получатель смарт-контракт&lt;/h4&gt;&lt;p&gt;EOA, который прикрепил к себе смарт-контракт, для ERC-721, ERC-777, нативной валюты, начинает вести себя, как смарт-контракт.&lt;p&gt;Это означает, что EOA не сможет принимать активы, если на прикрепленном смарт-контракте не реализованы функции:&lt;pre&gt;&lt;code class=javascript&gt;receive() external payable {}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;или&lt;pre&gt;&lt;code class=javascript&gt;function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes _data) external returns(bytes4);&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;и так далее.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3e1/76b/0a7/3e176b0a717fc6b3cc2edfdfa714e0fd.png alt=&#34;Проблема при получении некоторых видов активов&#34; title=&#34;Проблема при получении некоторых видов активов&#34; width=1210 height=380 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3e1/76b/0a7/3e176b0a717fc6b3cc2edfdfa714e0fd.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3e1/76b/0a7/3e176b0a717fc6b3cc2edfdfa714e0fd.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Проблема при получении некоторых видов активов&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Для проверки можно прикрепить к любому аккаунту пустой смарт-контракт &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/receiveNativeCurrency/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.sol&lt;/a&gt;, который не будет реализовывать функцию &lt;code&gt;receive()&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;contract Delegation {}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Проверочный тест будет выглядеть следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;function test_checkSendNativeCurrency(uint256 value) external {&#xA;    Vm.SignedDelegation memory signedDelegation = vm.signDelegation(address(delegation), user.key);&#xA;&#xA;    // Operator прикрепляет смарт-контракт Delegation к user&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.attachDelegation(signedDelegation);&#xA;    vm.stopBroadcast();&#xA;&#xA;    // Пытаемся отправить нативную валюту, транзакция ревертнется&#xA;    (bool success,) = user.addr.call{value: value}(&amp;#34;&amp;#34;); // success == false&#xA;&#xA;    assertFalse(success);&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Полный пример теста &lt;code&gt;test_checkSendNativeCurrency&lt;/code&gt; в &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/receiveNativeCurrency/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.t.sol&lt;/a&gt;.&lt;h4&gt;Конфликт переменных хранилища для EOA&lt;/h4&gt;&lt;p&gt;EOA может делегировать выполнение разным смарт-контрактам: прикрепляя и открепляя их. Вновь прикрепленный смарт-контракт может легко повлиять на переменные хранилища, которые были записаны предыдущим смарт-контрактом.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/420/bdd/de1/420bddde1f41733378c5a8b8fdf2502c.png alt=&#34;Коллизия в хранилище&#34; title=&#34;Коллизия в хранилище&#34; width=1448 height=510 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/420/bdd/de1/420bddde1f41733378c5a8b8fdf2502c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/420/bdd/de1/420bddde1f41733378c5a8b8fdf2502c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Коллизия в хранилище&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Для проверки это гипотезы поочередно прикрепим два смарт-контракта &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/storageCollision/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;DelegationFirst.sol&lt;/a&gt; и &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/storageCollision/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;DelegationSecond.sol&lt;/a&gt;, которые работают с первым слотом памяти: первый устанавливает &lt;code&gt;uint256&lt;/code&gt;, второй &lt;code&gt;bytes32&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;contract DelegationFirst {&#xA;    uint256 private _value;&#xA;&#xA;    function setValue(uint256 newValue) external {&#xA;        _value = newValue;&#xA;    }&#xA;&#xA;    function getValue() external view returns (uint256) {&#xA;        return _value;&#xA;    }&#xA;}&#xA;&#xA;contract DelegationSecond {&#xA;    bytes32 private _hashValue;&#xA;&#xA;    function setHash(bytes32 hashValue) external {&#xA;        _hashValue = hashValue;&#xA;    }&#xA;&#xA;    function getHash() external view returns (bytes32) {&#xA;        return _hashValue;&#xA;    }&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Проверочный тест будет выглядеть следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;function test_storageCollision(uint256 value, bytes32 hashValue) external {&#xA;    // Прикрепляем первый смарт-контракт&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.signAndAttachDelegation(address(delegationFirst), user.key);&#xA;    vm.stopBroadcast();&#xA;&#xA;    // Устанавливаем значение в хранилище смарт-контракта&#xA;    DelegationFirst(user.addr).setValue(value);&#xA;    assertEq(DelegationFirst(user.addr).getValue(), value);&#xA;&#xA;    // Прикрепляем второй смарт-контракт&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.signAndAttachDelegation(address(delegationSecond), user.key);&#xA;    vm.stopBroadcast();&#xA;&#xA;    // Устанавливаем hash в хранилище смарт-контракта&#xA;    DelegationSecond(user.addr).setHash(hashValue);&#xA;    assertEq(DelegationSecond(user.addr).getHash(), hashValue);&#xA;&#xA;    // Прикрепляем первый смарт-контракт повторно&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.signAndAttachDelegation(address(delegationFirst), user.key);&#xA;    vm.stopBroadcast();&#xA;&#xA;    assertNotEq(DelegationFirst(user.addr).getValue(), value); // Доказывает что первоначальное значение было изменено&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Полный пример теста &lt;code&gt;test_test_storageCollision&lt;/code&gt; в &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/storageCollision/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.t.sol&lt;/a&gt;.&lt;p&gt;&lt;strong&gt;Что делать с коллизией?&lt;/strong&gt;&lt;p&gt;Единственным решением здесь может быть введение особого пространства наименования переменных, чтобы исключить пересечение слотов.&lt;p&gt;Идея заключается в том, что вместо использования последовательных слотов хранения (это работает в Solidity по умолчанию) использовать &lt;a href=https://eips.ethereum.org/EIPS/eip-7201 rel=&#34;noopener noreferrer nofollow&#34;&gt;ERC-7201 Storage Namespaces Explained&lt;/a&gt; или другие подобные решения.&lt;p&gt;Чтобы использовать этот шаблон, разработчику необходимо придумать базовый идентификатор, который будет использоваться для генерации хеша, который будет идентификатором для слота хранилища.&lt;pre&gt;&lt;code class=javascript&gt;contract Delegation {&#xA;    struct MainStorage {&#xA;        uint256 value;&#xA;    }&#xA;&#xA;    // keccak256(abi.encode(uint256(keccak256(&amp;#34;MetaLampIsTheBest&amp;#34;)) - 1)) &amp;amp; ~bytes32(uint256(0xff))&#xA;    bytes32 public constant MAIN_STORAGE_LOCATION = 0xd66e0df2d96f7ee8f3c31d9f50f7a36abdf7b3a8a2cbbfbe615a3abcc8b5af00;&#xA;&#xA;    function _getMainStorage() private pure returns (MainStorage storage $) {&#xA;        assembly {&#xA;            $.slot := MAIN_STORAGE_LOCATION&#xA;        }&#xA;    }&#xA;&#xA;    function getValue() external view returns (uint256) {&#xA;        MainStorage storage $ = _getMainStorage();&#xA;&#xA;        // Получаем значение из хранилища, которое хранится в специальном слоте&#xA;        return $.value;&#xA;    }&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;em&gt;Важно!&lt;/em&gt; EIP-7201 не защитит, если пользователь прикрепил к своему аккаунту вредоносный смарт-контракт, который будет целенаправленно переписывать определенный слот вашего правильного хранилища.&lt;p&gt;Также, как никто не застрахован от подписания транзакции, которая прикрепит неизвестный код к EOA.&lt;h4&gt;Работа с нативной валютой&lt;/h4&gt;&lt;p&gt;Рассмотрим два случая для аккаунта, который прикрепил себе смарт-контракт:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Была вызвана payable функция на прикрепленном смарт-контракте в контексте аккаунта пользователя&lt;li&gt;&lt;p&gt;Была вызвана payable функция на прикрепленном смарт-контракте в контексте аккаунта пользователя, которая отправила нативную валюту на другой смарт-контракт.&lt;/ol&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c49/b19/463/c49b19463e135c250dc13fc3903cbd71.png alt=&#34;Транзит нативной валюты сквозь payable функции&#34; title=&#34;Транзит нативной валюты сквозь payable функции&#34; width=1456 height=576 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c49/b19/463/c49b19463e135c250dc13fc3903cbd71.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c49/b19/463/c49b19463e135c250dc13fc3903cbd71.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Транзит нативной валюты сквозь payable функции&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В первом случае, нативная валюта останется на балансе пользователя, во-втором - на балансе смарт-контракта, который был финальным получателем.&lt;p&gt;Для проверки это гипотезы прикрепим к пользователю смарт-контракт &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/work-with-native-currency/Delegation.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.sol&lt;/a&gt;.&lt;pre&gt;&lt;code class=javascript&gt;contract Delegation {&#xA;    /// Оставляем нативную валюту на адресе user&#xA;    function buy() external payable {}&#xA;&#xA;    /// @notice Пересылаем нативную валюту на смарт-контракт target&#xA;    function buyAndSendToTarget(address target) external payable {&#xA;        (bool success, ) = target.call{value: msg.value}(&amp;#34;&amp;#34;);&#xA;&#xA;        if (!success) {&#xA;            revert();&#xA;        }&#xA;    }&#xA;}&#xA;&#xA;contract Target {&#xA;    // Разрешаем принимать нативную валюту&#xA;    receive() external payable {}&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Проверочных теста будет два:&lt;pre&gt;&lt;code class=javascript&gt;function test_workWithNativeCurrency_buy(uint256 value) external {&#xA;    deal(operator.addr, value);&#xA;&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.signAndAttachDelegation(address(delegation), user.key);&#xA;    vm.stopBroadcast();&#xA;&#xA;    vm.prank(operator.addr);&#xA;    Delegation(user.addr).buy{value: value}();&#xA;&#xA;    // Нативная валюта остается на балансе пользователя&#xA;    assertEq(user.addr.balance, value);&#xA;}&#xA;&#xA;function test_workWithNativeCurrency_buyAndSendToTarget(uint256 value) external {&#xA;    deal(operator.addr, value);&#xA;&#xA;    vm.startBroadcast(operator.key);&#xA;    vm.signAndAttachDelegation(address(delegation), user.key);&#xA;    vm.stopBroadcast();&#xA;&#xA;    vm.prank(operator.addr);&#xA;    Delegation(user.addr).buyAndSendToTarget{value: value}(address(target));&#xA;&#xA;    // Нативная валюта остается на балансе target смарт-контракта&#xA;    assertEq(address(target).balance, value);&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Полный пример тестов &lt;code&gt;test_workWithNativeCurrency_buy&lt;/code&gt; и &lt;code&gt;test_workWithNativeCurrency_buyAndSendToTarget&lt;/code&gt; можно найти в &lt;a href=https://github.com/fullstack-development/blockchain-wiki/blob/main/EIPs/eip-7702/contracts/work-with-native-currency/Delegation.t.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Delegation.t.sol&lt;/a&gt;.&lt;h3&gt;Совместимость&lt;/h3&gt;&lt;p&gt;Один из самых интересных моментов, как EIP-7702 будет чувствовать себя с предыдущими решениями по абстракции аккаунта, в частности с &lt;a href=https://eips.ethereum.org/EIPS/eip-4337 rel=&#34;noopener noreferrer nofollow&#34;&gt;ERC-4337&lt;/a&gt;.&lt;p&gt;На удивление очень хорошо! Все, кто имел абстрактные аккаунты на базе ERC-4337, могут продолжать использовать их. Дело в том, что для ERC-4337 не принципиален инициатор транзакции, достаточно того, чтобы критерии проверки (подпись user operation) были валидны.&lt;p&gt;В свою очередь, ребята из eth-infinitism практически сразу предложили вариант аккаунта в котором в одной связке работают ERC-4337 и ERC-7702. Первый стандарт для спонсирования газа, второй для батчинга операций.&lt;p&gt;Выглядит такой аккаунт следующим образом:&lt;pre&gt;&lt;code class=javascript&gt;contract Simple7702Account is BaseAccount, IERC165, IERC1271, ERC1155Holder, ERC721Holder {&#xA;    /// Адрес entrypoint, который может вызывать транзакции на аккаунте пользователя&#xA;    function entryPoint() public pure override returns (IEntryPoint) {&#xA;        return IEntryPoint(0x4337084D9E255Ff0702461CF8895CE9E3b5Ff108);&#xA;    }&#xA;&#xA;    /// Здесь будет проверяться, что операция подписана приватным ключом пользователя&#xA;    function _validateSignature(&#xA;        PackedUserOperation calldata userOp,&#xA;        bytes32 userOpHash&#xA;    ) internal virtual override returns (uint256 validationData) {&#xA;&#xA;        return _checkSignature(userOpHash, userOp.signature) ? SIG_VALIDATION_SUCCESS : SIG_VALIDATION_FAILED;&#xA;    }&#xA;&#xA;    ...&#xA;}&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Схематично работа ERC-4337 и EIP-7702 представлена на схеме ниже.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/8f0/308/ffe/8f0308ffec32379d025a519552de7cfe.png alt=&#34;ERC-4337 &amp;amp; EIP-7702&#34; title=&#34;ERC-4337 &amp;amp; EIP-7702&#34; width=1068 height=456 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/8f0/308/ffe/8f0308ffec32379d025a519552de7cfe.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/8f0/308/ffe/8f0308ffec32379d025a519552de7cfe.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;ERC-4337 &amp;amp; EIP-7702&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Пользователь делегирует выполнение &lt;strong&gt;Simple7702Account&lt;/strong&gt; (прикрепляет код этого смарт-контракта). После этого он может подписывать данные об операциях и через &lt;strong&gt;bundler&lt;/strong&gt; передавать их на выполнение.&lt;p&gt;&lt;a href=https://github.com/eth-infinitism/account-abstraction/blob/releases/v0.8/contracts/accounts/Simple7702Account.sol rel=&#34;noopener noreferrer nofollow&#34;&gt;Полный пример такого аккаунта&lt;/a&gt;.&lt;p&gt;В EIP-7702 вцепились все заинтересованные в абстракции: viem, metamask, ZeroDev, Biconomy, Alchemy, Trust Wallet, Ambire и другие. Они так или иначе интегрировали EIP-7702 в свои решения, поэтому референсных имплементаций достаточное количество.&lt;h3&gt;Etherscan&lt;/h3&gt;&lt;p&gt;Etherscan тоже молодец и сразу поддержал EIP-7702. Для этого он вывел все транзакции четвертого типа на отдельную &lt;a href=https://etherscan.io/txnAuthList rel=&#34;noopener noreferrer nofollow&#34;&gt;страницу&lt;/a&gt;.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/4b7/386/4ee/4b73864ee907571943ecd6dea16667bd.png alt=&#34;Etherscan: отображение Delegated Address&#34; title=&#34;Etherscan: отображение Delegated Address&#34; width=2766 height=1252 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/4b7/386/4ee/4b73864ee907571943ecd6dea16667bd.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/4b7/386/4ee/4b73864ee907571943ecd6dea16667bd.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Etherscan: отображение Delegated Address&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Также etherscan показывает, что транзакция выполнена через прикрепленный смарт-контракт, которому EOA делегировал выполнение.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/425/d00/8b5/425d008b5c78ffad7bf3eb34b435cb2a.png alt=&#34;Etherscan: отображение Delegated Address внутри транзакции&#34; title=&#34;Etherscan: отображение Delegated Address внутри транзакции&#34; width=2870 height=1388 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/425/d00/8b5/425d008b5c78ffad7bf3eb34b435cb2a.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/425/d00/8b5/425d008b5c78ffad7bf3eb34b435cb2a.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Etherscan: отображение Delegated Address внутри транзакции&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Увидеть, что транзакция выполнялась на прикрепленном смарт-контракте можно и в отдельном поле &amp;#34;Delegated Address&amp;#34;, а увидеть тип транзакции в поле &amp;#34;Other Attributes&amp;#34;.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/298/6c2/88b/2986c288b7c5322f3486ab955cacbee5.png alt=&#34;Etherscan: поле Other Attributes&#34; title=&#34;Etherscan: поле Other Attributes&#34; width=2070 height=1028 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/298/6c2/88b/2986c288b7c5322f3486ab955cacbee5.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/298/6c2/88b/2986c288b7c5322f3486ab955cacbee5.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Etherscan: поле Other Attributes&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h3&gt;Недостатки&lt;/h3&gt;&lt;p&gt;Нужно понимать, что EIP-7702 не является реализацией полной абстракции аккаунта, он не преобразует EOA в &amp;#34;Умный самодостаточный аккаунт&amp;#34;.&lt;p&gt;С точки зрения абстракции аккаунта, EIP-7702 имеет ряд ограничений:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Приватный ключ все еще очень важен&lt;/strong&gt;: это сохраняет полный контроль над аккаунтом, одновременно выступая бекдором. Поэтому защита приватного ключа крайне важна.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Неравнозначные права для мультисига&lt;/strong&gt;: По-прежнему нельзя создать полноценный мультисиг на базе EIP-7702, так как у владельцев будут разные права. Всем придется доверять первоначальному EOA. Его приватный ключ наделяет его неограниченной властью.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ограниченное восстановление аккаунта&lt;/strong&gt;: если приватный ключ утерян или скомпрометирован, восстановить полный контроль над EOA невозможно. Единственным решением будет замена приватного ключа, но это нетривиальная задача.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Отсутствие квантовой устойчивости&lt;/strong&gt;: в будущем потребуется перейти на полностью квантово-устойчивые аккаунты. EOA же по-прежнему уязвимы для потенциальных квантовых алгоритмов, которые могут скомпрометировать их приватные ключи.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Невозможность использования аккаунта в качестве эскроу-счета&lt;/strong&gt;: потому что первоначальный EOA имеет неограниченные полномочия и может изъять средства.&lt;/ul&gt;&lt;h3&gt;Вывод&lt;/h3&gt;&lt;p&gt;Таким образом, EIP-7702 является важным, стратегическим шагом на пути к абстракции аккаунта, привносит новую энергию в экосистему Ethereum и позволяет внедрять новые сценарии. Как абстракция аккаунта будет выглядеть в будущем одному Виталику известно.&lt;p&gt;Также не стоит забывать ключевые проблемы:&lt;ul&gt;&lt;li&gt;&lt;p&gt;коллизии при работе с хранилищем аккаунта&lt;li&gt;&lt;p&gt;сложности при работе в разных сетях&lt;li&gt;&lt;p&gt;необходимость помнить про поддержку ERC-721 или ERC-777, поддержка нативной валюты&lt;li&gt;&lt;p&gt;невозможность реализовать полноценный мультисиг или эскроу-счет&lt;li&gt;&lt;p&gt;приватный ключ по-прежнему имеет решающее значение&lt;/ul&gt;&lt;p&gt;Полный переход EOA на смарт-аккаунты потребует дальнейших изменений в Ethereum. По мере развития экосистемы EIP-7702 будет играть важнейшую роль в улучшение UI кошельков, повышении безопасности для пользователей, приближая нас к конечной цели — полной абстракции аккаунтов.&lt;h2&gt;Links&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Ethereum Improvement Proposals: &lt;a href=https://eips.ethereum.org/EIPS/eip-7702 rel=&#34;noopener noreferrer nofollow&#34;&gt;EIP-7702: Set Code for EOAs&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://www.cyfrin.io/glossary/eip-7702 rel=&#34;noopener noreferrer nofollow&#34;&gt;EIP-7702&lt;/a&gt; от Cyfrin&lt;li&gt;&lt;p&gt;&lt;a href=https://www.youtube.com/live/NZQc6bQdW9g rel=&#34;noopener noreferrer nofollow&#34;&gt;Solidity и Ethereum, урок #93 | EIP-7702: EOA code&lt;/a&gt;. Видео от Ilya Krukowski. Здесь практический опыт использования EIP-7702 со стороны смарт-контрактов&lt;li&gt;&lt;p&gt;&lt;a href=https://slowmist.medium.com/in-depth-discussion-on-eip-7702-and-best-practices-968b6f57c0d5 rel=&#34;noopener noreferrer nofollow&#34;&gt;In-Depth Discussion on EIP-7702 and Best Practices&lt;/a&gt; от SlowMist. Здесь больше со стороны того, как это работает в Ethereum&lt;li&gt;&lt;p&gt;&lt;a href=https://viem.sh/docs/eip7702 rel=&#34;noopener noreferrer nofollow&#34;&gt;Документация viem&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://info.etherscan.com/what-you-need-to-know-about-eip-7702-smart-account/ rel=&#34;noopener noreferrer nofollow&#34;&gt;What You Need to Know About EIP-7702 “Smart” Account&lt;/a&gt; от etherscan&lt;li&gt;&lt;p&gt;&lt;a href=&#34;https://www.youtube.com/watch?v=ZFN2bYt9gNE&amp;amp;ab_channel=TheRedGuild&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;Deep Dive into Ethereum 7702 Smart Accounts: security risks, footguns and testing&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://medium.com/@BahadorGh/emergency-eip-7702-wallet-recovery-f4cc865f6341 rel=&#34;noopener noreferrer nofollow&#34;&gt;Emergency EIP-7702 Wallet Recovery&lt;/a&gt;. История о том, как случайное делегирование позволило увести активы с кошелька.&lt;li&gt;&lt;p&gt;&lt;a href=https://eip7702.io/ rel=&#34;noopener noreferrer nofollow&#34;&gt;EIP-7702 overview&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://medium.com/valixconsulting/eip7702-closing-the-gap-between-eoas-and-smart-contracts-16b6f05584a9 rel=&#34;noopener noreferrer nofollow&#34;&gt;EIP7702: Closing the Gap Between EOAs and Smart Contracts&lt;/a&gt;. Здесь интересен исторический обзор, как эволюционировали стандарты в области абстракции аккаунтов.&lt;li&gt;&lt;p&gt;&lt;a href=https://github.com/fireblocks-labs/awesome-eip-7702 rel=&#34;noopener noreferrer nofollow&#34;&gt;Awesome EIP-7702&lt;/a&gt;. На случай, если нужно еще больше материалов&lt;/ol&gt;&lt;p&gt;&lt;em&gt;Мы с командой делимся мыслями и наблюдениями в &lt;/em&gt;&lt;a href=https://t.me/+8p2iaLV6_tBmNTZi rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;em&gt;своём Telegram-канале&lt;/em&gt;&lt;/a&gt;&lt;em&gt;. Не всё доходит до статей, что-то проще оформить в пост, чем расписывать длинный текст. Если интересно, чем живём, что обсуждаем и с какими задачами сталкиваемся, то заглядывайте.&lt;/em&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>pnaydanovgoo</author>
      <guid>https://habr.com/ru/articles/1029386/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029386</guid>
      <pubDate>Thu, 30 Apr 2026 13:40:30 +0000</pubDate>
    </item>
    <item>
      <title>Анализ защищенности 15 лет спустя. Акт первый</title>
      <link>https://habr.com/ru/companies/angarasecurity/articles/1013986/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1013986</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Всем привет! Меня зовут Сергей и я&lt;strong&gt; &lt;/strong&gt;директор Центра Исследования Киберугроз в Angara Security. Занимаюсь и руковожу пентестами и редтимами всю свою сознательную ИБ-карьеру.&lt;p&gt;Хочу с вами, наши дорогие и уважаемые читатели, поделиться своими мыслями насчет подмены понятий в услугах по анализу защищенности. Очень часто я видел (да и сейчас часто вижу), как пентест называют &amp;#34;сканированием уязвимостей&amp;#34; или в понятие Red Team закладывают самый обычный пентест, пусть и с элементами &amp;#34;противодействия&amp;#34;.&lt;div class=floating-image&gt;&lt;p&gt;Не вдаваясь в подробности моей мотивации, я решил сделать цикл статей, где на основе своего опыта и опыта моих коллег постараюсь разложить весь offensive, с примерами и объяснениями действительно важных моментов, которые влияют на качество, длительность и стоимость таких работ, с разбором реальных кейсов из моей практики и прочими вещами. Сразу оговорюсь, я постараюсь это сделать в легком, ненапряжном формате, чтобы чтение этих статей не превратилось в унылое занятие и читателям не захотелось бы вместо чтения пойти &lt;em&gt;«&lt;/em&gt;позалипать в рилсы&lt;em&gt;»&lt;/em&gt; или заняться более полезными вещами. Ждать от статей какого-то рокетсаенса и разборов хардкорных техник не стоит. Наоборот, это будет скорее простой и разжеванный материал на более широкую и менее погруженную аудиторию.&lt;/div&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Спойлер для тех, кто все-таки хочет вдаться в подробности мотивации&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;И все же, что меня сподвигло. Приведу один короткий, но показательный пример из моей реальной практики:&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e85/aa8/5f9/e85aa85f9cc9d3a9b66caf010c18c4b2.png alt=&#34;Суровые будни лида пентестеров...&#34; title=&#34;Суровые будни лида пентестеров...&#34; width=759 height=564 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e85/aa8/5f9/e85aa85f9cc9d3a9b66caf010c18c4b2.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e85/aa8/5f9/e85aa85f9cc9d3a9b66caf010c18c4b2.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Суровые будни лида пентестеров...&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Слева - мой давний товарищ и очень зрелый эксперт из Defensive Secuirty, от которого такой вопрос получить было, мягко говоря, неожиданно. Справа - рабочий чат с одним из клиентов в процессе проекта. Несмотря на отсутствие контекста, суть проблемы эти переписки отражают. Я бы понял и не обращал на это внимание, если бы это был 1 случай на 20-30 проектов. Но такое встречается с завидной регулярностью по сей день.&lt;p&gt;С одной стороны, это может быть не критично. Особенно, когда и заказчик и исполнитель ожидают от работ одного и того же. С другой стороны, такая &amp;#34;синхронизация&amp;#34; встречается не так часто, как хотелось бы. И это приводит не просто к недопониманию на уровне оперативного общения, а скорее и чаще к тому, что одна сторона (заказчик) ожидает одного результата, а другая сторона (исполнитель) выдает совсем другой результат. А иногда это &amp;#34;недопонимание&amp;#34; происходит на уровне анализа ТЗ и пресейла. Одни не понимают, почему это стоит дороже и проводится дольше ожидаемого, а другие не понимают почему условия в ТЗ противоречат сложившейся практике или что хочет от них заказчик в принципе. В конечном итоге никто не получает желаемого.&lt;p&gt;В связи с этим у меня (и не только) возникает непреодолимое желание рассказать миру, как все-таки понять этот нелегкий мир услуг offensive security, выделить для себя отличия в этих услугах и сделать вывод о пользе того или иного подхода, решая свои задачи. Конечно же, я далеко не первый (и не последний), кто пытается сделать то же самое. В сети есть определенное количество крутых статей, подкастов, видео-материалов, где так или иначе рассказывается все то же самое, и мои коллеги с &lt;em&gt;«&lt;/em&gt;красной стороны&lt;em&gt;»&lt;/em&gt; действительно сделали крутую работу, недооценивать которую я не хочу и не имею права (привет и респект ребятам из PT, Kaspersky, BI.Zone, Cicada8, УЦСБ, Singleton и множества других компаний). Вместе с этим, в каждом из этих материалов так или иначе (как минимум мне) не хватает каких-то нюансов для охвата всей картины: где-то техники, где-то организационки, где-то банальной базы, потому что расчет был на достаточно зрелую аудиторию. И чтобы сложить всю картину у себя в голове, нужно не только пересмотреть/перечитать/послушать все, но и местами даже самому заняться изучением вопроса. Это драгоценное время, которое у нас всегда в дефиците.&lt;/div&gt;&lt;/details&gt;&lt;p&gt;Начать цикл статей я бы хотел с цитаты французского мыслителя Вольтера:&lt;div class=persona&gt;&lt;img class=&#34;image persona__image&#34; src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/a34/b17/b04/a34b17b041786bbe7becbee6b99c4330.jpg sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/a34/b17/b04/a34b17b041786bbe7becbee6b99c4330.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/a34/b17/b04/a34b17b041786bbe7becbee6b99c4330.jpg 781w&#34; loading=lazy decode=async&gt;&lt;h5 class=persona__heading&gt;Вольте́р, Франсуа́-Мари́ Аруэ́&lt;/h5&gt;&lt;p class=persona__text&gt;французский писатель и философ, один из главных представителей просветительской мысли XVIII века&lt;/div&gt;&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;«&lt;/em&gt;Тот, кто не знает прошлого, не знает ни настоящего, ни будущего, ни самого себя&lt;em&gt;»&lt;/em&gt;&lt;/blockquote&gt;&lt;p&gt;Что-то слишком пафосно... В общем, давайте&lt;strong&gt; &lt;/strong&gt;начнем с истории offensive security на нашем рынке. Я надеюсь, что знание того, с чего все начиналось, в дальнейшем поможет понять те или иные нюансы современных пентестов и редтимов. К тому же такой краткий экскурс будет интересен молодым перспективным безопасникам, а олды поностальгируют и, возможно, даже смахнут скупую слезу.&lt;p&gt;Итак, поехали!&lt;h2&gt;История offensive-услуг&lt;/h2&gt;&lt;p&gt;Вернемся в (уже) далекий конец 00-х, начало 10-х годов 21 века. Информационная безопасность только набирала обороты, пентест как таковой еще не был так популярен как сейчас. Предлагали эту услугу немногие компании (навскидку я вспомнил только Информзащиту, ДиалогНаука, и Диджитал Секьюрити), а о внутренних пентест-командах в крупных компаниях было известно чуть меньше, чем ничего. Качество работ определялось уровнем энтузиазма пентестеров и количеством выполненных проектов. Усредненный процесс пентеста (например, внутреннего) на тот момент был примерно следующим:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Приезжаем на объект;&lt;li&gt;&lt;p&gt;Проводим сетевые атаки типа ARP Spoofing или VLAN-hopping. Может быть, если кто-то был знаком с Responder (да-да, первый коммит на Github датируется аж 12 февраля 2013 года) - NBT-NS/LLMNR spoofing;&lt;li&gt;&lt;p&gt;Запускаем легендарный &lt;em&gt;«&lt;/em&gt;сине-зеленый&lt;em&gt;»&lt;/em&gt; сканер уязвимостей или пресловутый Xspider на все доступные подсети;&lt;li&gt;&lt;p&gt;Cмотрим результаты:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Нашлось что-то? Супер, пробуем это эксплуатировать, вручную считаем CVSS, думаем над рекомендациями;&lt;li&gt;&lt;p&gt;Не нашлось - штош, идем писать про &lt;em&gt;«&lt;/em&gt;Уровень защищенности - высокий&lt;em&gt;»;&lt;/em&gt;&lt;/ul&gt;&lt;/ul&gt;&lt;div class=floating-image&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/761/0ba/d61/7610bad611cf4c349e94dc8c414cf0ed.png alt=&#34;Примерный флоу любого внутряка тех времен&#34; title=&#34;Примерный флоу любого внутряка тех времен&#34; width=934 height=783 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/761/0ba/d61/7610bad611cf4c349e94dc8c414cf0ed.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/761/0ba/d61/7610bad611cf4c349e94dc8c414cf0ed.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Примерный флоу любого внутряка тех времен&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Спойлер для тех, кто мог бы обидеться...&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Я не хочу сказать, что так делали все. Это усредненный пример, который варьировался от исполнителя к исполнителю. Поэтому пока отложите свои тапки, которыми хотите запустить в меня :)&lt;/div&gt;&lt;/details&gt;&lt;p&gt;С внешним пентестом было примерно то же самое, только вышеупомянутый сканер дополнялся еще одним &lt;em&gt;«&lt;/em&gt;бело-красным&lt;em&gt;»&lt;/em&gt; сканером версии 10.5 для веб-приложений &lt;span class=habrahidden&gt;(помните, как он стал клиент-серверным?)&lt;/span&gt;. Атаки на Kerberos? AD CS? Kubernetes? Нет, тогда этого еще не было. Хотя в 2017 году сильно нашумела история с ShadowBrokers и уязвимостью EternalBlue и с тех пор в арсенале у расторопных пентестеров, помимо metasploit&amp;#39;а, появилась своя виртуалочка с FuzzBunch.&lt;p&gt;Почему это было именно так? Для себя я выделил следующие причины:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Отсутствие достаточного опыта на рынке.&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;Пентест, как методичная и формализованная активность, только созревал. Это нормально, никогда не получается сразу &lt;em&gt;«&lt;/em&gt;сделать красиво&lt;em&gt;»&lt;/em&gt;.&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Уровень развития технологий и универсальность.&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;В то время не было разделения экспертов на направления, как это сейчас. Пентестеры были своего рода универсалами-ремесленниками, которые умели и &lt;em&gt;«&lt;/em&gt;апрспуфинг&lt;em&gt;»&lt;/em&gt; провести, и кавычку в форму веб-приложения пихнуть. Ну и, справедливости ради, на тот момент и веб был не таким тяжеловесным и по большей части серверсайдным, не было такого количества JavaScript-фреймворков, средства защиты еще не были такими &lt;em&gt;«&lt;/em&gt;злыми&lt;em&gt;»&lt;/em&gt;, как сегодня, а наше комьюнити еще так глубоко не изучило&lt;strong&gt; &lt;/strong&gt;винду и Active Directory. Быть универсалом на тот момент было в какой-то степени нормой.&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;u&gt;Отсутствие адаптированной методической и практической базы.&lt;/u&gt;&lt;/strong&gt;&lt;br&gt;Опытные&lt;strong&gt; &lt;/strong&gt;безопасники мне, конечно, могут возразить: &lt;em&gt;«&lt;/em&gt;Сережа, ты не прав! Был тогда и OWASP, и NIST 800-115, и PTES&lt;em&gt;»&lt;/em&gt;. Не спорю, были, но есть небольшой нюанс, и на этом пункте давайте остановимся подробнее.&lt;/ul&gt;&lt;p&gt;Действительно, все эти стандарты и методики на тот момент были, но не у нас. Не секрет, что мировые тренды до наших краев доходят с некоторой задержкой. Сейчас эта задержка, безусловно, сократилась до недель. Но мы же говорим про 10-е года. Попробуем найти аргументов в пользу этого и пройдемся по стандартам, которые чаще всего на слуху, могут быть практически применены и вы их видели (или даже сами писали) в отчетах по пентесту: OWASP, PTES, OSSTMM.&lt;h4&gt;OWASP Testing Guide&lt;/h4&gt;&lt;p&gt;История этого гайда &lt;a href=https://owasp.org/www-project-web-security-testing-guide/v41/6-Appendix/E-History&gt;начинается в 2004 году&lt;/a&gt;. Однако &lt;strong&gt;&lt;s&gt;его&lt;/s&gt;&lt;/strong&gt; более-менее используемым он стал примерно со второй версии, которую &lt;a href=https://owasp.org/www-project-web-security-testing-guide/assets/archive/OWASP_Testing_Guide_v2.pdf&gt;зарелизили в 2007 году&lt;/a&gt;. К нам он дошел ближе к началу 10-х годов. Была даже глобальная кампания по переводу этого гайда на разные языки мира, включая русский. Об этом писали &lt;a href=https://habr.com/ru/articles/245537/&gt;на Хабре&lt;/a&gt;. Обратим внимание на дату - 2014 год.&lt;br&gt;&lt;br&gt;Сам гайд содержит 270+ страниц и применить на практике его, как некий читшит, было (да и есть) достаточно сложно.&lt;br&gt;&lt;br&gt;Поэтому некоторые пентестеры делали свои локальные читшиты на основе этого гайда и своего опыта. Впоследствии появился отдельный проект &lt;a href=https://cheatsheetseries.owasp.org/index.html&gt;OWASP Cheat Sheet&lt;/a&gt;, который уже более-менее стал утилитарным. Правда, он появился &lt;a href=https://github.com/OWASP/CheatSheetSeries/commit/b1cce7034c755afe715bc59fb112e0c64e449785&gt;только к 2018 году&lt;/a&gt; :) И, что немаловажно, данный гайд был полезен для тестирования приложений, но не покрывал &lt;em&gt;«&lt;/em&gt;инфраструктуру&lt;em&gt;»&lt;/em&gt; в достаточной мере.&lt;h4&gt;PTES&lt;/h4&gt;&lt;p&gt;Данный стандарт впервые &lt;a href=https://pentest-standard.readthedocs.io/en/latest/faq.html#q-what-is-this-penetration-testing-execution-standard&gt;был опубликован в 2009 году&lt;/a&gt;, и в отличие от того же OWASP Testing Guide больше описывал этапы процесса и рекомендуемые инструменты (список которых часто копипастился в &lt;em&gt;«&lt;/em&gt;Перечень используемых инструментов&lt;em&gt;»&lt;/em&gt; в тех же отчетах по пентесту). Опять же, использовать его как читшит или мануал было достаточно сложно, потому что текст PTES не подразумевает конкретики по тому или иному действию, а местами даже общая информация отсутствует.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/277/c2e/e00/277c2ee00cdb495e8ba7c35aa9bfeab0.png alt=&#34;Шел 2026 год...&#34; title=&#34;Шел 2026 год...&#34; width=1624 height=965 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/277/c2e/e00/277c2ee00cdb495e8ba7c35aa9bfeab0.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/277/c2e/e00/277c2ee00cdb495e8ba7c35aa9bfeab0.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Шел 2026 год...&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h4&gt;OSSTMM&lt;/h4&gt;&lt;p&gt;Еще один зарубежный стандарт, первая версия которого появилась в конце 2000 года (для тех, кто хочет посмотреть одну из первых версий - &lt;a href=https://dl.packetstormsecurity.net/papers/unix/osstmm.pdf&gt;сюда&lt;/a&gt;), а третья версия (ныне актуальная) - &lt;a href=https://www.isecom.org/OSSTMM.3.pdf&gt;в конце 2010 года&lt;/a&gt;. Опять же, если обратиться к тексту стандарта, то вы вряд ли найдете подробную информацию, например, как и чем эффективно выполнить фаззинг веб-приложения или как корректно сделать ARP spoofing, чтобы не завернуть весь трафик подсети через свой интерфейс и не положить сетку всего этажа офиса Заказчика на некоторое время. &lt;span class=habrahidden&gt;(опытным пентестерам должна быть знакома эта ситуация)&lt;/span&gt; Скорее, вы больше там увидите концепцию подхода к тестированию, начиная от определений (безопасности, комплаенса, границ, контролей и т.д) и заканчивая требований к результату, то есть что вы должны написать в отчет и в каком виде.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3e6/4fd/e0b/3e64fde0bdc61cde8e5005da3d512a53.png alt=&#34;Не похоже на те отчеты, что вы писали или получали, правда?&#34; title=&#34;Не похоже на те отчеты, что вы писали или получали, правда?&#34; width=739 height=1020 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3e6/4fd/e0b/3e64fde0bdc61cde8e5005da3d512a53.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3e6/4fd/e0b/3e64fde0bdc61cde8e5005da3d512a53.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Не похоже на те отчеты, что вы писали или получали, правда?&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Был еще ряд других стандартов, методик и справочников (например, &lt;a href=https://casa.sandia.gov/idart/&gt;IDART&lt;/a&gt;, &lt;a href=https://listings.pcisecuritystandards.org/documents/Penetration-Testing-Guidance-v1_1.pdf&gt;PCI DSS Penetration Testing Guidance&lt;/a&gt; или &lt;a href=https://repo.zenk-security.com/Techniques%20d.attaques%20%20.%20%20Failles/RTFM-%20Red%20Team%20Field%20Manual.pdf&gt;RTFM&lt;/a&gt;), но все они были по большей части скорее формальными документами и имели мало отношения к практической реализации пентеста. Да, я знаю что были уже тогда и наши отечественные стандарты и методики (привет 15408, 27001, СТО БР ИББС и другие), но они были куда более формальными, чем вышеперечисленные документы.&lt;p&gt;Но это все &amp;#34;бумажная теория&amp;#34;. Чтобы убедиться в своих выводах, я еще года 3 назад провел небольшой опрос среди своих друзей-знакомых, кто в те времена был рядовым пентестером.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c6c/e8d/9f2/c6ce8d9f218bbfbaa887bcda0f654bd9.png alt=&#34;Пациент номер раз&#34; title=&#34;Пациент номер раз&#34; width=676 height=738 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c6c/e8d/9f2/c6ce8d9f218bbfbaa887bcda0f654bd9.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c6c/e8d/9f2/c6ce8d9f218bbfbaa887bcda0f654bd9.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Пациент номер раз&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/161/41a/845/16141a845546be25a33f428d49df61f6.png alt=&#34;Пациент номер два (опечатался, бывает...)&#34; title=&#34;Пациент номер два (опечатался, бывает...)&#34; width=781 height=665 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/161/41a/845/16141a845546be25a33f428d49df61f6.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/161/41a/845/16141a845546be25a33f428d49df61f6.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Пациент номер два (опечатался, бывает...)&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div class=floating-image&gt;&lt;p&gt;Второй, кстати, верно замечает еще одну немаловажную вещь. Помимо методик и чек-листов, на тот момент не было такого разнообразия тренировочных площадок или образов для самостоятельного изучения азов хакинга. Я навскидку вспомнил лишь &lt;a href=&#34;https://www.root-me.org/?lang=en&#34;&gt;RootMe&lt;/a&gt;, &lt;a href=http://www.itsecgames.com/&gt;bWAPP&lt;/a&gt; и &lt;a href=https://docs.rapid7.com/metasploit/metasploitable-2/&gt;Metasploitable&lt;/a&gt; . Ну и, конечно же, множество CTF-соревнований на &lt;a href=http://CTFtime.org&gt;CTFtime.org&lt;/a&gt;. &lt;span class=habrahidden&gt;(но мы-то знаем, насколько &lt;em&gt;«&lt;/em&gt;близки&lt;em&gt;»&lt;/em&gt; условия CTF к реальной жизни...)&lt;/span&gt;&lt;p&gt;На сегодняшний день существует просто несметное количество блогов, читшитов, курсов и площадок, посвященных пентесту и редтиму. Перечислять их здесь не буду, ибо времени на это может не хватить. Да, раньше были курсы и от EC-Council (тот же CEH), и от eLearn Security. Но они стоили денег, в отличие от общедоступных и бесплатных блогов и Github-репозиториев.&lt;/div&gt;&lt;p&gt;Результат таких работ - отдельный вид искусства. Давайте посмотрим на скриншот части реального отчета 2015 года одной уважаемой ИБ-компании:&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/b60/5fe/49e/b605fe49ea80fe00a081f5df79dc3200.png alt=&#34;Ууух, RCE в винде...&#34; title=&#34;Ууух, RCE в винде...&#34; width=876 height=570 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/b60/5fe/49e/b605fe49ea80fe00a081f5df79dc3200.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/b60/5fe/49e/b605fe49ea80fe00a081f5df79dc3200.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Ууух, RCE в винде...&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Но если посмотреть на описание этой же уязвимости в результатах вышеупомянутого &lt;em&gt;«&lt;/em&gt;сине-зеленого&lt;em&gt;»&lt;/em&gt; сканера уязвимостей...&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/2e1/5de/58a/2e15de58ab3aeb5d568194b1b65dfdc5.png alt=Хмм... title=Хмм... width=965 height=508 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/2e1/5de/58a/2e15de58ab3aeb5d568194b1b65dfdc5.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/2e1/5de/58a/2e15de58ab3aeb5d568194b1b65dfdc5.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Хмм..&lt;/em&gt;.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;...то станет ясно, что раздел отчета - это перевод-калька описания уязвимости из сканера. И в отчетах того времени было достаточно много таких разделов. &lt;span class=habrahidden&gt;(припоминаете, да, кто в те времена покупал пентест?)&lt;/span&gt;&lt;br&gt;Оттуда примерно и пошел этот стереотип, что пентестеры без сканера уязвимостей не работают. Кого-то устраивал (а может, даже и устраивает по сей день) такой результат. В этом нет ничего плохого, такое и сейчас можно встретить. Только это будет не отчет о пентесте, а скорее отчет о сканировании уязвимостей.&lt;p&gt;Итого, что имеем в итоге:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Было не так много боевого опыта.&lt;li&gt;&lt;p&gt;Методическая база была, но не применима на практике.&lt;li&gt;&lt;p&gt;Практическая база у каждого была своя и уровень её экспертности зависел от энтузиазма пентестера.&lt;li&gt;&lt;p&gt;Пентестеры были &lt;em&gt;«&lt;/em&gt;универсалами&lt;em&gt;»&lt;/em&gt;.&lt;li&gt;&lt;p&gt;Технологии на тот момент времени были не такими сложными.&lt;/ol&gt;&lt;h2&gt;Разница услуг тогда и сейчас&lt;/h2&gt;&lt;p&gt;Вернемся в наше время. Если вы посещали известные конференции, где на большой площади стоит караван стендов с предложениями по услугам и продуктам, то однозначно&lt;strong&gt; &lt;/strong&gt;видели буклеты с описанием всех предлагаемых услуг. И сегодня информация об offensive-услугах зачастую не помещается на одной стороне листка формата А5. Там будут такие предложения, как:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Сканирование уязвимостей;&lt;li&gt;&lt;p&gt;Внешний пентест;&lt;li&gt;&lt;p&gt;Внутренний пентест;&lt;li&gt;&lt;p&gt;Анализ приложений (веб и мобильных);&lt;li&gt;&lt;p&gt;Соц.инженерия;&lt;li&gt;&lt;p&gt;Анализ Wi-Fi сетей;&lt;li&gt;&lt;p&gt;Физический пентест;&lt;li&gt;&lt;p&gt;CPT (он же Continuous Penetration Testing);&lt;li&gt;&lt;p&gt;Red Team;&lt;li&gt;&lt;p&gt;Purple Team;&lt;li&gt;&lt;p&gt;Киберучения.&lt;/ul&gt;&lt;p&gt;Для сравнения, предложения тех времен насчитывали примерно 3 услуги:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Пентест;&lt;li&gt;&lt;p&gt;Комплексный пентест;&lt;li&gt;&lt;p&gt;Анализ защищенности.&lt;/ul&gt;&lt;p&gt;И каждая современная услуга по сути стала самостоятельной, со своими требованиями, ограничениями и особенностями. Оно и логично. Технологии стали куда сложнее (только контейнеры и облачные инфраструктуры чего стоят), времени на изучение/анализ конкретного куска инфраструктуры уходит больше, количество угроз и известных уязвимостей с каждым днем только увеличивается. Это все еще сверху сдабривается уровнем зрелости и разнообразием средств защиты, что, безусловно, усложняет и удорожает атаку, но никогда не сводит ее вероятность к нулю. Отсюда и выходит, что на каждый тип работ нужны свои компетенции, свои временные рамки и другие нюансы. Приведу пару примеров для аргументации своих мыслей, а если еще мои коллеги по цеху в комментариях подтвердят (или опровергнут) приведенные примеры - будет вообще здорово.&lt;h4&gt;Внутренний пентест&lt;/h4&gt;&lt;p&gt;Пример ранних внутренних пентестов&lt;strong&gt; &lt;/strong&gt;я уже приводил выше. На тот момент не было известно и/или найдено такого количества уязвимостей, позволяющих полностью скомпрометировать всю инфраструктуру. Навскидку, из наиболее пробивных я могу вспомнить MS08-067 и MS17-010. Да и, откровенно говоря, в арсенале пентестера-инфраструктурщика было достаточно иметь сканер уязвимостей, metasploit и пару-тройку утилит типа Responder, THC-Hydra или bettercap, чтобы разломать инфраструктуру среднестатистического Заказчика.&lt;p&gt;Однако, после нашумевшего EternalBlue и выхода на сцену российского пентеста пресловутого Kerberoasting&amp;#39;а, (хотя официально он опубликован аж в 2014 году на &lt;a href=&#34;https://www.irongeek.com/i.php?page=videos/derbycon4/t120-attacking-microsoft-kerberos-kicking-the-guard-dog-of-hades-tim-medin&#34;&gt;Derbycon 2014&lt;/a&gt;, но первый коммит в Impacket скрипта &lt;a href=http://GetUserSPNs.py&gt;GetUserSPNs.py&lt;/a&gt; &lt;a href=https://github.com/fortra/impacket/commit/2a185c1ecc0b0a56467f12dfccbd5672ed95adaa&gt;был сделан в мае 2016 года&lt;/a&gt;, а до нас он докатился чуть позже) количество уязвимостей и атак на инфраструктуру Windows начало стремительно расти. К тому времени уже использовались ныне известные атаки типа UD/CD/RBCD, атаки на WSUS, ADCS, SCCM, Pre-2K атаки и т.д.&lt;p&gt;В процессе написания этого текста я решил воспользоваться благами технического прогресса и попросил нейросеть собрать мне такую статистику за последние 15+ лет. Что получилось:&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/709/2ec/a9c/7092eca9c7aa1bf28b1a8c6e71c73cee.png alt=&#34;Спасибо ЧатуЖПТ за кривую, но наглядную статистику&#34; title=&#34;Спасибо ЧатуЖПТ за кривую, но наглядную статистику&#34; width=1435 height=898 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/709/2ec/a9c/7092eca9c7aa1bf28b1a8c6e71c73cee.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/709/2ec/a9c/7092eca9c7aa1bf28b1a8c6e71c73cee.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Спасибо ЧатуЖПТ за кривую, но наглядную статистику&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Спойлер для любознательных и недоверчивых&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Дабы не грузить вас полным текстом ИИ-изысканий, поделюсь просто своим начальным промптом для тех, кто хочет сравнить результат с графиком выше:&lt;p&gt;&lt;code&gt;Подготовь мне статистику выявленных уязвимостей в ОС Windows и Active Directory за период 2010-2025 год. В статистику должны попасть только те уязвимости, которые относятся к классу RCE и Relay, активно эксплуатировались в реальных атаках и имеют публичные инструменты и описания в блогах исследователей по безопасности.&lt;br&gt;Дополни статистику перечнем атак на windows и Active Directory, которые мог быть не присвоен идентификатор CVE, но все равно использовались в реальных атаках и имеют публичное описание и инструменты эксплуатации. Например, атаки на Kerberos, LDAP, WSUS, SCCM и другие. То есть нужно определить, в какой год появилась та или иная атака и добавить эту информацию в статистику. В качестве источника таких атак можно использовать mindmap от Orange Cyberdefence (&lt;/code&gt;&lt;a href=https://orange-cyberdefence.github.io/ocd-mindmaps/&gt;&lt;code&gt;https://orange-cyberdefence.github.io/ocd-mindmaps/&lt;/code&gt;&lt;/a&gt;&lt;code&gt;), но не ограничиваться только им.&lt;br&gt;Для каждой уязвимости/атаки должна быть приведена ссылка на ресурс с описанием, а также ссылки на публично доступные инструменты и PoC.&lt;/code&gt;&lt;p&gt;&lt;code&gt;Статистика должна быть представлена в следующих видах:&lt;/code&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Таблица с колонками &amp;#34;Год&amp;#34;, &amp;#34;Названия уязвимостей&amp;#34;, &amp;#34;Номера CVE&amp;#34;, &amp;#34;Номера MS&amp;#34; (к примеру MS08-067), &amp;#34;Ссылки&amp;#34;, где каждая строка - год публикации уязвимости/атаки&lt;/code&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;Кумулятивный график обнаружения уязвимостей по годам, где ось X - годы, ось Y - количество уязвимостей&lt;/code&gt;&lt;/ol&gt;&lt;/div&gt;&lt;/details&gt;&lt;p&gt;С того момента (то есть где-то с 2017-2018 годов) арсенал пентестера уже далеко вышел за рамки метасплоита. Там уже начали появляться и Impacket, и BloodHound, и всякие ntlmrelayx c certyfy/certipy, и многое-многое другое. С того же момента уже физически один среднестатистический offensive-эксперт не мог удержать в голове информацию и по приложениям, и по инфраструктуре. Ну и времени на выполнение качественного внутреннего пентеста стало уходить пропорционально больше, что в совокупности с остальным и сделало его из «этапа» в самостоятельную экспертную услугу.&lt;h4&gt;Анализ приложений&lt;/h4&gt;&lt;p&gt;Общая картина аналогична инфраструктурному пентесту. На заре оффенсива веб-приложения были преимущественно&lt;strong&gt; &lt;/strong&gt;server-side&amp;#39;ными. Всю логику и безопасность обеспечивал сервер, а JavaScript в подавляющем большинстве случаев отвечал только за интерпретацию данных на стороне клиента. Впоследствии, когда грубо говоря XSS-ки начали активнее эксплуатировать ITW, разработчики стали уделять больше внимания client-side&amp;#39;у и ответственнее применять &lt;strong&gt;&lt;s&gt;всякие &lt;/s&gt;&lt;/strong&gt;SOP/CORS/CSP и прочие механизмы, которые также к сегодняшнему дню драматически усложнились.&lt;p&gt;Также в сложности в работу веб-пентестеров добавили, конечно же, фреймворки и языки программирования. Опять же, если обратиться к публичной статистике появления языков и фреймворков для разработки приложений (пусть даже не совсем точной и собранной нейросеткой), то можно увидеть динамику роста &lt;s&gt;зоопарка&lt;/s&gt; разнообразия технологий, с которыми приходится сталкиваться веберу. И чем свежее эта технология, тем вероятнее всего она разрабатывалась Secure by Design, а еще больше усложняет поиск уязвимостей в таком продукте. Приправим это сверху концепцией «многослойности».&lt;p&gt;Современное приложение - это уже не просто пачка PHP-файлов на файловой&lt;strong&gt; &lt;/strong&gt;системе сервера, а целый комбайн из технологий типа Kubernetes, S3, gRPC, GraphQL и т.д., который еще и работает за реверс-прокси. Да, можно справедливо заметить, что множество используемых технологий в некотором виде шаблонизированы и стандартизованы. Но отсюда только и вырастает сложность, так как при исключении простых уязвимостей типа SQL-инъекций, XXE или небезопасных десериалиазиций, появляются более сложные баги, поиск которых требует не только знания базовых вещей «на зубок», но и понимания работы той или иной технологии на уровне профессионального разработчика.&lt;p&gt;И не стоит упускать тот факт, что в целом культура безопасной разработки за последние 15 лет выросла и не идет в сравнение с тем, что было в начале 10-х годов.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/8f9/b81/504/8f9b81504722dafc23c43afcc2e56675.png alt=&#34;Спасибо (еще раз) ЧатуЖПТ за кривую, но наглядную статистику&#34; title=&#34;Спасибо (еще раз) ЧатуЖПТ за кривую, но наглядную статистику&#34; width=1401 height=893 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/8f9/b81/504/8f9b81504722dafc23c43afcc2e56675.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/8f9/b81/504/8f9b81504722dafc23c43afcc2e56675.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Спасибо (еще раз) ЧатуЖПТ за кривую, но наглядную статистику&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Спойлер для любознательных и недоверчивых&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Также делюсь своим начальным промптом, который я слегка подтюнил последующими вопросами:&lt;br&gt;&lt;br&gt;&lt;code&gt;Собери мне статистику появления языков программирования и фреймворков для разработки приложений, начиная с 2010 года и заканчивая 2025 годом. В статистике должны быть учтены популярные современные продукты, на основе которых сейчас разрабатывают веб и мобильные приложения. Статистика должна быть в двух видах:&lt;/code&gt;&lt;br&gt;&lt;br&gt;&lt;code&gt;1) Таблица с разбивкой по годам&lt;/code&gt;&lt;br&gt;&lt;code&gt;2) Кумулятивный график появления продуктов, где ось X - годы, ось Y - количество продуктов&lt;/code&gt;&lt;/div&gt;&lt;/details&gt;&lt;p&gt;Такие же примеры можно привести и для внешних пентестов, и для «вайфаев», и других услуг, но тогда чтение этой статьи превратится в унылое занятие. В общем, суть вы уловили.&lt;details class=spoiler&gt;&lt;summary&gt;Спойлер для тех, кто потянулся за тапком&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Я намеренно здесь не учитываю прогресс как самих средств защиты, так и зрелости ИБ-процессов в компаниях по причине того, что правильному пентесту, по-хорошему, не должны противодействовать. Но эту мысль я раскрою подробно в следующих статьях, не переживайте.&lt;/div&gt;&lt;/details&gt;&lt;h2&gt;Как с этим жить&lt;/h2&gt;&lt;p&gt;Скорее всего, у части читателей этой статьи возник вопрос: «И что дальше? Ну усложнилось всё, а мне что с этим дальше делать?». Справедливо, у меня бы тоже он возник. На этот и многие другие вопросы я отвечу в следующих статьях, где постараюсь подробно разобрать каждый из видов offensive-услуг в сравнении с другими.&lt;p&gt;Возможно, у вас есть и другие вопросы, которые вам было бы интересно разобрать в следующих материалах. Не стесняйтесь, пишите их в комментарии, а я постараюсь разобрать максимальное количество этих вопросов.&lt;p&gt;А пока, этим текстом мне хотелось погрузить читателя в очень краткий, местами субъективный экскурс offensive-услуг, который в дальнейшем поможет вам посмотреть немного под другим углом на те услуги, которые есть сейчас на рынке, и, я надеюсь, немного скорректировать ваше видение и понимание.&lt;p&gt;Stay tuned, до встречи в следующих статьях!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/angarasecurity/articles/1013986/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1013986</guid>
      <pubDate>Thu, 30 Apr 2026 13:36:14 +0000</pubDate>
    </item>
    <item>
      <title>Экономим нервные клетки: как передавать макеты в разработку</title>
      <link>https://habr.com/ru/companies/lemana_tech/articles/1026956/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1026956</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/cc3/38b/ea1/cc338bea143627e3f70635ed5fbee39e.jpg width=780 height=440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/cc3/38b/ea1/cc338bea143627e3f70635ed5fbee39e.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/cc3/38b/ea1/cc338bea143627e3f70635ed5fbee39e.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;&lt;br&gt;Я продуктовый дизайнер с опытом более шести лет. В Лемана Тех работаю над продуктами ITSM-системы и касс.&lt;p&gt;В 2025 году я лидировал разработку чек-листа для передачи макетов в разработку. В этой статье кратко расскажу про процесс создания, сложности и ошибки, а также дам артефакт — сам чек-лист.&lt;p&gt;Чек-лист появился как побочный продукт более глубокой проблемы — &lt;strong&gt;потерь качества и времени на стыке дизайна и разработки&lt;/strong&gt;. Важно: он изначально не задумывался как регламент или обязательное правило. Это был вспомогательный инструмент для снижения количества конфликтов, разночтений и улучшения коммуникации между ролями.&lt;p&gt;Это была не инициатива «давайте всё задокументируем» и не попытка внедрить стандарт ради стандарта. Была конкретная потребность — &lt;strong&gt;улучшить качество продукта на выходе&lt;/strong&gt;.&lt;p&gt;От дизайнеров я часто слышал, что разработчик сделал «не совсем так», как на макетах. Копнул чуть глубже — а там компоненты не из дизайн-системы, проблемы с отступами, стилями и прочее. И такие проблемы встречаются очень часто в большинстве команд (спасибо, кэп). Напрягает, что это не пытаются решить: это проблемы, о которых все знают, но редко говорят о них, как будто это норма — так и должно быть.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/93f/be3/46c/93fbe346cf7b123e8a5cf686618fb71b.png width=772 height=299 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/93f/be3/46c/93fbe346cf7b123e8a5cf686618fb71b.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/93f/be3/46c/93fbe346cf7b123e8a5cf686618fb71b.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h3&gt;Проблема&lt;/h3&gt;&lt;p&gt;Формально процесс выглядел корректно:&lt;ul&gt;&lt;li&gt;&lt;p&gt;дизайнер передаёт макеты;&lt;li&gt;&lt;p&gt;разработка начинает реализацию;&lt;li&gt;&lt;p&gt; в процессе возникают вопросы;&lt;li&gt;&lt;p&gt; часть решений принимается на ходу.&lt;/ul&gt;&lt;p&gt;На практике это приводило к системным симптомам:&lt;ul&gt;&lt;li&gt;&lt;p&gt;разработчики регулярно задают одни и те же вопросы;&lt;li&gt;&lt;p&gt; дизайнер злится, потому что «всё же есть на макетах»;&lt;li&gt;&lt;p&gt;часть решений принимается без участия дизайнера;&lt;li&gt;&lt;p&gt;итоговая реализация расходится с задуманной;&lt;li&gt;&lt;p&gt;правки обсуждаются, но не всегда доходят до реализации, чтобы не срывать сроки.&lt;/ul&gt;&lt;p&gt;Ключевая проблема была не в компетенциях и не в «плохом дизайнере» или «невнимательных разработчиках». Проблема была в &lt;strong&gt;отсутствии общего понимания&lt;/strong&gt;, что именно считается «достаточной передачей макетов».&lt;p&gt;Каждый действовал по-своему:&lt;ul&gt;&lt;li&gt;&lt;p&gt;дизайнер — из логики визуальной целостности и пользовательского флоу;&lt;li&gt;&lt;p&gt;разработчик — из логики реализации и технических ограничений;&lt;li&gt;&lt;p&gt;продакт и тимлид — из логики сроков и приоритетов.&lt;/ul&gt;&lt;p&gt;И нигде это не сходилось в одну точку.&lt;h3&gt;Решение&lt;/h3&gt;&lt;p&gt;Я начал не с документа. Первым шагом стало формирование рабочей группы из дизайнеров и разработчиков. Задача была простой и сложной одновременно — &lt;strong&gt;исследовать реальный опыт обеих сторон&lt;/strong&gt;.&lt;h4&gt;Исследование и выравнивание контекста&lt;/h4&gt;&lt;p&gt;В состав рабочей группы входили 10 дизайнеров и разработчиков разного уровня компетенций в своей роли. Вместе мы собирали референсы подобных чек-листов из других компаний и формировали список болей, которые есть у каждой из сторон при передаче макетов.&lt;p&gt;Работа была выстроена следующим образом: сначала каждый из участников фиксировал свои боли, потом мы все вместе обсуждали каждую из них и группировали по проблемам. Такие вопросы, как коммуникация в команде, споры при проведении дизайн-ревью, незнание горячих клавиш и функционала Figma, сразу были оставлены на потом, так как это большие темы, которые требуют погружения в каждую проблему.&lt;p&gt;По списку болей были составлены гипотезы, которые мы пошли проверять в глубинных интервью у разработчиков. Это были не просто интервью, а совмещённые с наблюдением. Сначала дизайнер наблюдал, как разработчик верстает по макету, задавал уточняющие вопросы. Далее дизайнер продолжал интервью по оставшимся гипотезам, которые не были выявлены при наблюдении.&lt;p&gt;Важно было понять не то, «как должно быть», а &lt;strong&gt;как есть на самом деле&lt;/strong&gt;.&lt;p&gt;Проблемы со стороны дизайнеров мы сравнивали с проблемами разработчиков. Нужно было понять, это недоработка макета или другая проблема, связанная с коммуникацией, процессами внутри команды или с незнанием инструмента.&lt;h4&gt;Опыт разработчиков&lt;/h4&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/fba/401/ccc/fba401cccaee7e736d97309746f1d2d5.png width=865 height=726 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/fba/401/ccc/fba401cccaee7e736d97309746f1d2d5.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/fba/401/ccc/fba401cccaee7e736d97309746f1d2d5.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Мы разобрали:&lt;ul&gt;&lt;li&gt;&lt;p&gt;как они читают макеты и верстают по ним;&lt;li&gt;&lt;p&gt;где чаще всего возникают вопросы;&lt;li&gt;&lt;p&gt;какие решения им приходится принимать самостоятельно.&lt;/ul&gt;&lt;p&gt;Выяснилось, что большая часть проблем возникает из-за отсутствия &lt;strong&gt;контекста решений&lt;/strong&gt;:&lt;ul&gt;&lt;li&gt;&lt;p&gt;как компонент ведёт себя в разных состояниях;&lt;li&gt;&lt;p&gt;как ориентироваться в слоях и структуре;&lt;li&gt;&lt;p&gt;где допустимы отклонения, а где нет.&lt;/ul&gt;&lt;h4&gt;Опыт дизайнеров&lt;/h4&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/1bf/9c2/bb7/1bf9c2bb7fd51a4e7d1d1d3303705380.png width=876 height=540 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/1bf/9c2/bb7/1bf9c2bb7fd51a4e7d1d1d3303705380.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/1bf/9c2/bb7/1bf9c2bb7fd51a4e7d1d1d3303705380.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Со стороны дизайнера картина была не менее показательной:&lt;ul&gt;&lt;li&gt;&lt;p&gt;уверенность, что «и так всё понятно»;&lt;li&gt;&lt;p&gt;слабое понимание документации дизайн-системы;&lt;li&gt;&lt;p&gt;собственная логика компонентов вместо системной;&lt;li&gt;&lt;p&gt;отсутствие ясности, какие вещи критичны для разработки;&lt;li&gt;&lt;p&gt;отсутствие согласованности, когда несколько дизайнеров работают над одним продуктом.&lt;/ul&gt;&lt;h4&gt;Формирование чек-листа&lt;/h4&gt;&lt;p&gt;Только после этого появился чек-лист. Он не отвечал на вопрос, как правильно делать дизайн. Он отвечал на другой, более приземлённый вопрос.&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Достаточно ли информации, чтобы разработка могла реализовать решение без догадок?&lt;/strong&gt;&lt;/blockquote&gt;&lt;p&gt;Чек-лист не проверял дизайн-решения, он проверял готовность к реализации и фиксировал:&lt;ul&gt;&lt;li&gt;&lt;p&gt;наличие всех состояний;&lt;li&gt;&lt;p&gt;проработку корнер-кейсов;&lt;li&gt;&lt;p&gt;понятность логики поведения;&lt;li&gt;&lt;p&gt;согласованность компонентов;&lt;li&gt;&lt;p&gt;технические ограничения и допущения.&lt;/ul&gt;&lt;p&gt;В ходе исследования стало ясно, что часть проблем решается не процессами, а базовым выравниванием инструментального контекста. Многие разработчики просто не знали, как эффективно пользоваться Figma: от горячих клавиш и навигации по слоям до перехода к основному компоненту и применения всех фишек Dev Mode. После появления чек-листа мы провели встречи, где дизайнеры объясняли свой контекст принятия решений, а разработчиков обучали тонкостям Figma (горячие клавиши, логика работы компонентов, слои и отступы).&lt;h3&gt;Ошибки и сложности&lt;/h3&gt;&lt;p&gt;Ценность чек-листа — в рекомендательном характере: он опора, напоминание и точка сверки, а не обязательное правило или инструмент управления поведением команд. В командах, где взаимодействие между дизайном и разработкой уже выстроено, попытка навязать единый сценарий передачи макетов воспринимается как избыточный контроль и вызывает сопротивление. В таком виде чек-лист перестаёт помогать и начинает мешать.&lt;p&gt;Основные сложности и ошибки в процессе создания:&lt;ul&gt;&lt;li&gt;&lt;p&gt;сложная фасилитация больших групп, где каждый тянет одеяло на себя;&lt;li&gt;&lt;p&gt;крайне низкий отклик на формы и опросы (около 5 человек);&lt;li&gt;&lt;p&gt;частичное использование чек-листа (10–15 человек);&lt;li&gt;&lt;p&gt;необходимость постоянного «пиара» материала, чтобы он оставался на слуху;&lt;li&gt;&lt;p&gt;изначально лишний ресурс, потраченный на асинхронный сбор обратной связи вместо живого обсуждения.&lt;/ul&gt;&lt;p&gt;Ключевые риски&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Превратить чек-лист в регламент — &lt;/strong&gt;формальность убивает смысл.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Использовать его как инструмент контроля.&lt;/strong&gt; Если чек-лист — способ «поймать на ошибке», доверие исчезает мгновенно.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Не обновлять&lt;/strong&gt;. Продукт меняется, команда меняется — чек-лист тоже должен эволюционировать.&lt;/ol&gt;&lt;h3&gt;Выводы&lt;/h3&gt;&lt;p&gt;Важным результатом стал не сам чек-лист. Исследование дало фокус, куда двигаться дальше. Стало понятно, что значительная часть проблем лежит вне документа. Со стороны разработчиков есть недостаток базовых знаний работы с Figma и непонимание ими логики дизайнерских решений. У дизайнеров отсутствует представление о реальных сложностях разработки, существует разрыв в коммуникациях между командами, работающими над одним продуктом, а также регулярный выход за рамки возможностей дизайн-системы. Чек-лист лишь подсветил эти зоны и задал направление для дальнейшей работы — обучения, коммуникации и выравнивания контекста.&lt;p&gt;Сейчас чек-лист больше помогает при онбординге новых сотрудников. Во многих командах со сложившимися процессами он способствует разрешению споров.&lt;p&gt;Если дизайнер не может передать решение так, чтобы его можно было реализовать, значит, решение ещё не закончено.&lt;p&gt;Чек-лист — лишь артефакт, а не волшебная таблетка. Он работает только в связке с живой коммуникацией и взаимным пониманием.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/0e4/948/d18/0e4948d18b7b14f87853efb6bb0406fa.png width=868 height=582 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/0e4/948/d18/0e4948d18b7b14f87853efb6bb0406fa.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/0e4/948/d18/0e4948d18b7b14f87853efb6bb0406fa.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h2&gt;Чек-лист передачи макетов в разработку&lt;/h2&gt;&lt;details class=spoiler&gt;&lt;summary&gt;1. Передача макета&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Есть апрув от команды. Вы договорились, что макеты готовы и идут в разработку.&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Сформирована правильная ссылка на макет.&lt;/strong&gt;&lt;p&gt;Это не должна быть просто ссылка на документ. Ссылка должна вести на конкретный фрейм, откуда разработчику надо начинать смотреть макет.&lt;/blockquote&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;2. Брейкпоинты&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Макеты должны быть спроектированы с учетом всех брейкпоинтов дизайн-системы (добавьте основные брейкпоинты из вашей ДС в этот пункт).&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Брейкпоинты могут быть изменены по согласованию с командой.&lt;/strong&gt;&lt;p&gt;Возможно в вашей команде на основе веб-аналитики выбраны другие популярные экраны. Или вы проектируете интерфейс под конкретные устройства, например, кассы.&lt;/blockquote&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;3. Компоненты&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;В макетах использованы компоненты из дизайн системы, если они отвечают потребностям функционала.&lt;p&gt;&lt;strong&gt;Полезные ссылки (сюда нужно добавить ссылки из вашей ДС):&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;библиотека веб-компонентов в Фигме&lt;li&gt;&lt;p&gt;библиотеки мобильных компонентов в Фигме&lt;li&gt;&lt;p&gt; Storybook и Github — готовые компоненты в коде&lt;/ul&gt;&lt;p&gt;Если есть различия компонента от логики в документации, нужно подсветить это в макетах.&lt;p&gt;Ссылки на корпоративные ресурсы — куда писать, если есть вопросы по компонентам.&lt;p&gt;&lt;strong&gt;Если используются кастомные компоненты&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Новый компонент должен быть создан с учётом общепринятых паттернов;&lt;li&gt;&lt;p&gt;Описана логика работы. Дана ссылка на описание или показано на макете стрелкой;&lt;li&gt;&lt;p&gt;Добавлены все состояния: Default, Hover, Pressed, Disabled, Focused;&lt;li&gt;&lt;p&gt;Учтены пограничные состояния. Например, в текстовом блоке минимум/максимум символов, прописаны ограничения по кол-ву символов в полях ввода, учтены и описаны самые длинные и короткие тексты;&lt;li&gt;&lt;p&gt;Учтены размеры всех целевых экранов пользователей;&lt;li&gt;&lt;p&gt;Компоненты стилизованы под дизайн-систему.&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;4. Пустые состояния форм и корнер-кейсы&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Учтены на основных разрешениях экранов (см. пункт 2);&lt;li&gt;&lt;p&gt;Отрисованы состояния переполнения контейнеров;&lt;li&gt;&lt;p&gt;Сделаны шиммеры (скелетоны) или лоадеры экранов и блоков: определён с командой порядок загрузки частей страницы. Если нужно, отрисована поэтапная подгрузка или заглушки;&lt;li&gt;&lt;p&gt;Показаны состояния скролла;&lt;li&gt;&lt;p&gt;Описаны состояния валидации в формах ввода текста и написан текст к ошибкам валидации;&lt;li&gt;&lt;p&gt;Описан текст к снэкбарам: успех / неуспех;&lt;li&gt;&lt;p&gt;Спроектированы экраны ошибок 4ХХ, 5ХХ;&lt;li&gt;&lt;p&gt;Проведена редактура текста: все формулировки проверены и исправлены согласно Редполитике, макеты содержат актуальный и реальный текст или максимально приближенный, в тексте нет ошибок.&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;5. Слои&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Нет лишних вложенностей&lt;/strong&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/042/531/0ab/0425310ab8d84642d4b08d70e203f2f0.png alt=&#34;✅ Правильно&#34; title=&#34;✅ Правильно&#34; width=728 height=215 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/042/531/0ab/0425310ab8d84642d4b08d70e203f2f0.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/042/531/0ab/0425310ab8d84642d4b08d70e203f2f0.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;✅ Правильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3ea/0d5/64c/3ea0d564c3396dfc689d4cc8c9e7acda.png alt=&#34;❌ Неправильно&#34; title=&#34;❌ Неправильно&#34; width=777 height=203 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3ea/0d5/64c/3ea0d564c3396dfc689d4cc8c9e7acda.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3ea/0d5/64c/3ea0d564c3396dfc689d4cc8c9e7acda.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;❌ Неправильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Нет скрытых слоёв, если они не скрыты свойством компонента Boolean&lt;/strong&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ef9/4f1/1b1/ef94f11b1b5c1640609d7424df61b602.png alt=&#34;👀 Пример&#34; title=&#34;👀 Пример&#34; width=379 height=703 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ef9/4f1/1b1/ef94f11b1b5c1640609d7424df61b602.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ef9/4f1/1b1/ef94f11b1b5c1640609d7424df61b602.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;👀 Пример&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Не использованы Group внутри компонента без необходимости (кроме иллюстраций)&lt;/strong&gt;&lt;p&gt;Иллюстрации обёрнуты во фрейм, без дробных значений. Растровые изображения нужно также оборачивать во фрейм.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/695/374/a43/695374a438c146e1b61a34dc014ed0e9.png alt=&#34;☑️ Допустимо&#34; title=&#34;☑️ Допустимо&#34; width=770 height=466 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/695/374/a43/695374a438c146e1b61a34dc014ed0e9.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/695/374/a43/695374a438c146e1b61a34dc014ed0e9.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;☑️ Допустимо&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3e0/100/c80/3e0100c80aac7232b2359c65596a33de.png alt=&#34;❌ Неправильно&#34; title=&#34;❌ Неправильно&#34; width=701 height=430 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3e0/100/c80/3e0100c80aac7232b2359c65596a33de.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3e0/100/c80/3e0100c80aac7232b2359c65596a33de.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;❌ Неправильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;6. Консистентность&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;В макетах единая консистентность на всех экранах продукта&lt;ul&gt;&lt;li&gt;&lt;p&gt;Отступы;&lt;li&gt;&lt;p&gt;Скругления углов;&lt;li&gt;&lt;p&gt;Стили цветов и шрифтов;&lt;li&gt;&lt;p&gt;Одинаковая логика работы элементов;&lt;li&gt;&lt;p&gt;Например, поведение кнопок, раскрывающегося списка, навигации, кастомных компонентов или виджетов должно быть одинаково на всех экранах.&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Примечание&lt;/strong&gt;&lt;p&gt;Особенно у команд, которые взаимодействуют друг с другом, реализуют отдельные части большого продукта. Например, когда много продуктовых команд и много микрофронтов, нужно учитывать перечисленные выше пункты между командами. &lt;/blockquote&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;7. Описание макетов&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;В макете четко выделена часть, передаваемая в разработку&lt;ul&gt;&lt;li&gt;&lt;p&gt;Отмечена актуальность: какой экран или элемент актуальный, а какой ещё в работе;&lt;li&gt;&lt;p&gt;Экраны и их элементы рекомендуется передавать в сегменте, который можно целиком передать в разработку;&lt;li&gt;&lt;p&gt;По договорённости с командой разработки макет можно разделить на итерации и каждую итерацию поместить в свой сегмент.&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Должен прослеживаться сценарий с пошаговым объяснением действий пользователя&lt;/strong&gt;&lt;p&gt;Рекомендуется сегментировать: должны быть выделены части по JTBD или по задаче. Видеоописание и анимированный прототип сценария приветствуется, но &lt;strong&gt;не исключает&lt;/strong&gt; необходимости описания сценария или конкретной задачи в самом макете.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/a92/d13/dd9/a92d13dd9618de81ddabfc7b375b39ac.png alt=&#34;Каждая секция — это отдельная задача / сценарий / JTBD / user flow.&#34; title=&#34;Каждая секция — это отдельная задача / сценарий / JTBD / user flow.&#34; width=367 height=176 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/a92/d13/dd9/a92d13dd9618de81ddabfc7b375b39ac.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/a92/d13/dd9/a92d13dd9618de81ddabfc7b375b39ac.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Каждая секция — это отдельная задача / сценарий / JTBD / user flow.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e48/104/452/e48104452e31fac1f46c6657a8d2e21c.png alt=&#34;Описание задачи / сценария / JTBD / user flow&#34; title=&#34;Описание задачи / сценария / JTBD / user flow&#34; width=1330 height=618 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e48/104/452/e48104452e31fac1f46c6657a8d2e21c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e48/104/452/e48104452e31fac1f46c6657a8d2e21c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Описание задачи / сценария / JTBD / user flow&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Пояснения располагаются рядом с местом, к которому они относятся&lt;ul&gt;&lt;li&gt;&lt;p&gt;Пояснения могут соединяться стрелками с вариантами, другими пояснениями и экранами, важно, чтобы они были чёткие и по делу;&lt;li&gt;&lt;p&gt;В пояснения можно заносить информацию, взятую из интервью или технических ограничений: как должно работать, как должно мапится, откуда должна приходить информация, чтобы быть корректно реализованой.&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;8. Размеры&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;Размеры компонентов и вложенных элементов выражены целыми значениями. Объекты, которые имеют дробные значения (например, иконки или изображения), должны находиться в контейнерах.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/450/02f/dc3/45002fdc3e3e3d2eb0e00e5f567c8a92.png alt=&#34;✅ Правильно&#34; title=&#34;✅ Правильно&#34; width=307 height=87 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/450/02f/dc3/45002fdc3e3e3d2eb0e00e5f567c8a92.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/450/02f/dc3/45002fdc3e3e3d2eb0e00e5f567c8a92.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;✅ Правильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ed8/a4f/bea/ed8a4fbea7901fcf5e53d6d1473cfd2b.png alt=&#34;❌ Неправильно&#34; title=&#34;❌ Неправильно&#34; width=304 height=84 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ed8/a4f/bea/ed8a4fbea7901fcf5e53d6d1473cfd2b.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ed8/a4f/bea/ed8a4fbea7901fcf5e53d6d1473cfd2b.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;❌ Неправильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Расстояния между элементами и сами элементы должны быть кратны 2 px.&lt;/strong&gt;&lt;p&gt;Иначе на проде элементы могут отображаться некорректно.&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;9. Цвет и стили&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Все стили цвета указаны переменными.&lt;/strong&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d3d/371/17c/d3d37117c3395fc71047d8fa816eafa9.png alt=&#34;✅ Правильно&#34; title=&#34;✅ Правильно&#34; width=233 height=293 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d3d/371/17c/d3d37117c3395fc71047d8fa816eafa9.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d3d/371/17c/d3d37117c3395fc71047d8fa816eafa9.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;✅ Правильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/22e/cb2/e9a/22ecb2e9ab6a7704b75772adafa2ea67.png alt=&#34;❌ Неправильно&#34; title=&#34;❌ Неправильно&#34; width=212 height=289 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/22e/cb2/e9a/22ecb2e9ab6a7704b75772adafa2ea67.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/22e/cb2/e9a/22ecb2e9ab6a7704b75772adafa2ea67.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;❌ Неправильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Все шрифты указаны переменными.&lt;/strong&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/861/14a/148/86114a1481833a9b7cd20c7fbe1fb153.png alt=&#34;✅ Правильно&#34; title=&#34;✅ Правильно&#34; width=236 height=76 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/861/14a/148/86114a1481833a9b7cd20c7fbe1fb153.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/861/14a/148/86114a1481833a9b7cd20c7fbe1fb153.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;✅ Правильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f90/6c8/904/f906c89040cc2e6a79524e1cd8dc04dd.png alt=&#34;❌ Неправильно&#34; title=&#34;❌ Неправильно&#34; width=236 height=200 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f90/6c8/904/f906c89040cc2e6a79524e1cd8dc04dd.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f90/6c8/904/f906c89040cc2e6a79524e1cd8dc04dd.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;❌ Неправильно&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Если используется шрифт или цвет не из дизайн-системы, создайте локальные стили и пометьте это для разработчиков.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/566/b29/04d/566b2904d241564d16c12682459c9c80.png width=253 height=284 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/566/b29/04d/566b2904d241564d16c12682459c9c80.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/566/b29/04d/566b2904d241564d16c12682459c9c80.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;10. Анимация&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Если анимацию нужно реализовывать разработке, то она должна быть покадрово собрана и описана, или нужно передать собранный LottieFile;&lt;li&gt;&lt;p&gt;По договорённости с командой разработки можно для примера найти на другом ресурсе анимацию, которую надо реализовать.&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Важно!&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;h3&gt;Не вносить правки в макеты, которые отданы в разработку!&lt;/h3&gt;&lt;p&gt;Если всё же требуются изменения, то лучше сделать следующей итерацией. При этом нужно уведомить об изменениях разработку и дать новую ссылку на макеты.&lt;/div&gt;&lt;/details&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/lemana_tech/articles/1026956/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1026956</guid>
      <pubDate>Thu, 30 Apr 2026 13:30:11 +0000</pubDate>
    </item>
    <item>
      <title>Попробовали научить AI искать то, чего никто не замечает — слабые рыночные сигналы</title>
      <link>https://habr.com/ru/articles/1030160/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030160</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;h2&gt;Как мы научили алгоритм слушать рынок&lt;/h2&gt;&lt;blockquote&gt;&lt;p&gt;Это история об одном эксперименте, одной красивой ошибке и шести настоящих открытиях. О том, что наука — это не только правильные ответы. Это ещё и смелость задавать неудобные вопросы самому себе.&lt;/blockquote&gt;&lt;hr&gt;&lt;h3&gt;Мысль, с которой всё началось&lt;/h3&gt;&lt;p&gt;Представьте себе такую картину.&lt;p&gt;Утро. Вы открываете телефон и читаете новость: &lt;em&gt;«Нефть достигла 117 долларов за баррель»&lt;/em&gt;. Первый порыв — что-то сделать. Купить акции нефтяников? Продать рубли?&lt;p&gt;Но вот в чём секрет, который знают опытные аналитики: к тому моменту, как вы дочитали этот заголовок, тысячи компьютеров по всему миру уже отреагировали на эту новость. Миллисекунды назад. Очевидное движение уже случилось.&lt;p&gt;Значит ли это, что всё потеряно? Нет.&lt;p&gt;Потому что есть другой класс зависимостей. Не «нефть выросла — нефтяники выросли». А вот такая длинная, неочевидная цепочка:&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;нефть выросла сегодня&lt;/em&gt; &lt;em&gt;→ через месяц рубль укрепился&lt;/em&gt; &lt;em&gt;→ инфляция замедлилась&lt;/em&gt; &lt;em&gt;→ Центральный банк снизил ставку&lt;/em&gt; &lt;em&gt;→ банкам стало меньше зарабатывать&lt;/em&gt; &lt;em&gt;→ через три месяца после нефтяного пика финансовый сектор пошёл вниз&lt;/em&gt;&lt;/blockquote&gt;&lt;p&gt;Три месяца. Четыре причинно-следственных шага. К тому моменту, как эта связь становится очевидной всем — её уже объясняют задним числом: &lt;em&gt;«ну конечно, так и должно было быть»&lt;/em&gt;.&lt;p&gt;Но три месяца назад — никто не ставил.&lt;p&gt;Такие зависимости мы называем &lt;strong&gt;слабыми сигналами&lt;/strong&gt;. Они существуют в данных. Их можно найти. Просто нужна система, которая ищет их методично — перебирает тысячи комбинаций, проверяет сотни временны́х сдвигов, не устаёт и не отвлекается.&lt;p&gt;Человек так не может. Алгоритм — может.&lt;p&gt;Вот с чего начинался наш проект.&lt;hr&gt;&lt;h3&gt;Змея, которая кусает себя за хвост&lt;/h3&gt;&lt;p&gt;Нам нужна была идея — не просто алгоритм, а принцип. Способ думать об этой задаче.&lt;p&gt;И мы её нашли.&lt;p&gt;Мы вдохновились проектом &lt;a href=https://joi-lab.github.io/ouroboros/ rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;strong&gt;Ouroboros&lt;/strong&gt;&lt;/a&gt; — исследовательским агентом, который умеет учиться в цикле: генерировать гипотезы, проверять их, оценивать результат и передавать знание следующей итерации. Уроборос — это древний символ змеи, кусающей себя за хвост. Бесконечный круг, в котором конец становится началом.&lt;p&gt;Мы взяли эту идею и адаптировали её для финансовых данных.&lt;p&gt;Наш агент работает по тому же принципу:&lt;pre&gt;&lt;code&gt;гипотеза → SQL → данные → оценка&#xA;    ↑                        |&#xA;    └────── знания ←─────────┘&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Каждый круг — это итерация. Агент придумывает гипотезу, пишет запрос к базе данных, получает числа, оценивает их — и передаёт результат следующему кругу. Не скрипт, который запускают один раз. Живой цикл с памятью.&lt;p&gt;Причём агент учится сразу на &lt;strong&gt;трёх уровнях&lt;/strong&gt;:&lt;pre&gt;&lt;code&gt;┌─────────────────────────────────────────────────────────────┐&#xA;│  Уровень 3: между сессиями                                  │&#xA;│  каждая итерация → датасет → обучение следующей версии      │&#xA;│                                                             │&#xA;│  ┌──────────────────────────────────────────────────────┐   │&#xA;│  │  Уровень 2: внутри сессии                            │   │&#xA;│  │  гипотеза → SQL → данные → оценка → новая гипотеза   │   │&#xA;│  │                                                      │   │&#xA;│  │  ┌────────────────────────────────────────────────┐  │   │&#xA;│  │  │  Уровень 1: внутри одного запроса              │  │   │&#xA;│  │  │  ошибка → понять тип → исправить → попробовать │  │   │&#xA;│  │  └────────────────────────────────────────────────┘  │   │&#xA;│  └──────────────────────────────────────────────────────┘   │&#xA;└─────────────────────────────────────────────────────────────┘&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Первый уровень — невидимый. База данных строгая: неправильный тип, несуществующая колонка, деление на ноль. Агент сам классифицирует ошибку, сам исправляет, сам пробует снова. Вы не видите ничего — только результат.&lt;p&gt;Второй уровень — основной цикл. Именно здесь рождаются гипотезы.&lt;p&gt;Третий уровень — инвестиция в будущее. Каждая итерация пишет строку в базу данных. Со временем это становится материалом для обучения следующей, более умной версии агента.&lt;p&gt;Архитектура слоев данных и функциями&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/0a5/3a4/2db/0a53a42dbb7b1c5d1cc0cc522d5107f9.png alt=&#34;Архитектура Signal Mind&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/0a5/3a4/2db/0a53a42dbb7b1c5d1cc0cc522d5107f9.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/0a5/3a4/2db/0a53a42dbb7b1c5d1cc0cc522d5107f9.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Архитектура Signal Mind&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;hr&gt;&lt;h3&gt;Данные — это половина науки&lt;/h3&gt;&lt;p&gt;Прежде чем говорить об экспериментах, нужно поговорить о том, на чём они ставятся.&lt;p&gt;Мы собрали данные за несколько месяцев. Это скучная, но очень важная работа — как геолог, который годами собирает образцы, прежде чем сделать открытие.&lt;p&gt;Вот что у нас есть:&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Источник&lt;th&gt;&lt;p align=left&gt;Что внутри&lt;th&gt;&lt;p align=left&gt;Объём&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Московская биржа&lt;td&gt;&lt;p align=left&gt;15 индексов, 2016–2026&lt;td&gt;&lt;p align=left&gt;38 000+ строк&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Центральный банк РФ&lt;td&gt;&lt;p align=left&gt;Ключевая ставка, курс USD/RUB&lt;td&gt;&lt;p align=left&gt;5 600+ строк&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Росстат&lt;td&gt;&lt;p align=left&gt;Зарплаты, демография, жильё&lt;td&gt;&lt;p align=left&gt;15 503 строки&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Глобальные рынки&lt;td&gt;&lt;p align=left&gt;Brent, SP500, Gold, DXY, MSCI_INDIA…&lt;td&gt;&lt;p align=left&gt;13 инструментов&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Новости&lt;td&gt;&lt;p align=left&gt;Reuters, Bloomberg, CNBC и другие&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;2 520 591 статья&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Документы ЦБ&lt;td&gt;&lt;p align=left&gt;PDF-доклады, корпоративная отчётность&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;17 492 фрагмента&lt;/strong&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Почти 2.5 миллиона статей. Почти 10 гигабайт новостного текста. Загрузка заняла несколько часов. Этот файл мы не удаляем никогда — это первоисточник.&lt;p&gt;Перед каждой гипотезой агент делает кое-что важное: он читает документы ЦБ. Буквально — ищет в них нужный контекст. &lt;em&gt;Что регулятор думал об этой теме? Что писали в официальных бюллетенях?&lt;/em&gt; Это называется RAG, и это разница между «алгоритм нашёл число» и «алгоритм понял, что за ним стоит».&lt;p&gt;Когда мы загрузили журнал наших экспериментов в Obsidian — специальную программу для визуализации связей — получилась вот такая карта знаний:&lt;figure&gt;&lt;img src=https://habr.com/images/px.gif#https%3A%2F%2Fraw.githubusercontent.com%2FiRatG%2Fsignal-mind%2Fmaster%2Fhabr%2Fsample%2Fcharts%2Fobsidian.gif alt=&#34;Граф знаний Signal Mind в Obsidian&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habr.com/images/px.gif#https%3A%2F%2Fraw.githubusercontent.com%2FiRatG%2Fsignal-mind%2Fmaster%2Fhabr%2Fsample%2Fcharts%2Fobsidian.gif 780w,&#xA;       https://habr.com/images/px.gif#https%3A%2F%2Fraw.githubusercontent.com%2FiRatG%2Fsignal-mind%2Fmaster%2Fhabr%2Fsample%2Fcharts%2Fobsidian.gif 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Граф знаний Signal Mind в Obsidian&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;em&gt;Каждый узел — это гипотеза или источник данных. Линии — подтверждённые связи. Так выглядят 1943 итерации, собранные в одну картину.&lt;/em&gt;&lt;p&gt;Красиво, правда? Но чтобы это получить — сначала нужно было провести эксперимент.&lt;hr&gt;&lt;h3&gt;Ночь тишины&lt;/h3&gt;&lt;p&gt;Итак - час ночи.&lt;p&gt;Мы запустили агента и закрыли ноутбук.&lt;pre&gt;&lt;code class=bash&gt;python -m src.agent.watchdog 32400   # 9 часов&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Watchdog — это маленькая программа-сторож, которая раз в минуту проверяет: «агент ещё работает?» Если нет — перезапускает. Мы написали её на случай, если что-то пойдёт не так посреди ночи.&lt;p&gt;Она не понадобилась ни разу.&lt;p&gt;Девять часов агент работал в полной тишине. Генерировал гипотезы, писал SQL, получал числа, оценивал, передавал знание дальше. Без нас. Без подсказок.&lt;p&gt;В 10 утра мы открыли отчёт:&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Что мерили&lt;th&gt;&lt;p align=left&gt;Результат&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Итераций за ночь&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;1 943&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Токенов потрачено&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;409 миллионов&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Стоимость всей ночи&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;$5.76&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Среднее время одной итерации&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;14.4 секунды&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Ошибок SQL, которые агент исправил сам&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;22&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Из них — неудачных&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Перезапусков сторожа&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;0&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;«Подтверждённых» гипотез&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;67.7%&lt;/strong&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Шесть долларов. Девять часов. Почти две тысячи итераций.&lt;p&gt;И последняя строка — 67.7% — нас насторожила.&lt;hr&gt;&lt;h3&gt;Ошибка, которая стала подарком&lt;/h3&gt;&lt;p&gt;67.7% подтверждённых гипотез — это очень много.&lt;p&gt;Слишком много.&lt;p&gt;Настоящие рынки сложные. Шума больше, чем сигнала. Если система соглашается с семью гипотезами из десяти — значит, либо она гениальна, либо она что-то мерит неправильно.&lt;p&gt;Мы начали проверять вручную. Открывали каждый «подтверждённый» SQL и запускали его на живой базе данных. И примерно на третьем примере поняли.&lt;hr&gt;&lt;p&gt;Агент должен был взять данные по китайскому индексу вот так:&lt;pre&gt;&lt;code class=sql&gt;-- Правильно: данные из нужной таблицы&#xA;FROM market_data WHERE instrument = &amp;#39;FTSE_CHINA_50&amp;#39;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;А он делал вот так:&lt;pre&gt;&lt;code class=sql&gt;-- Что он реально писал:&#xA;SELECT imoex_close AS ftse_china_50   -- ← просто переименовал колонку&#xA;FROM v_market_context&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Понимаете, что произошло?&lt;p&gt;Он брал данные российского индекса IMOEX — и называл их «китайским индексом». Потом измерял корреляцию между IMOEX и MOEXOG. Между двумя российскими индексами. Конечно, они коррелируют — r ≈ 0.88. Потому что оба российские, а не потому что есть какой-то сигнал.&lt;p&gt;Та же ошибка повторялась для американского доллара, индийского рынка, южноафриканских акций. Агент не обманывал — он честно делал то, что умел. Просто в схеме было слепое пятно.&lt;p&gt;&lt;strong&gt;Около 60% «подтверждённых» сигналов были тавтологией.&lt;/strong&gt;&lt;p&gt;Реальный результат: не 67.7%, а &lt;strong&gt;примерно 15–25%&lt;/strong&gt;.&lt;hr&gt;&lt;p&gt;Теперь самое важное — что мы сделали дальше.&lt;p&gt;Мы не удалили эти примеры.&lt;p&gt;Многие исследователи в такой ситуации стараются «почистить» данные — убрать плохие примеры, чтобы красиво выглядела статистика. Мы сделали наоборот.&lt;p&gt;Плохие примеры тоже попали в обучающий датасет — с пометкой «это ошибка, вот почему». Потому что именно из таких пар «неправильный вопрос → правильный ответ» и учится по-настоящему умная система.&lt;p&gt;Ошибка превратилась в знание. Это и есть суть третьего уровня обучения.&lt;blockquote&gt;&lt;p&gt;Хорошая наука — это не когда всё идёт правильно. Это когда ошибки замечают, понимают и делают из них выводы.&lt;/blockquote&gt;&lt;hr&gt;&lt;h3&gt;Исправление&lt;/h3&gt;&lt;p&gt;После находки мы сделали три вещи.&lt;p&gt;&lt;strong&gt;Первое&lt;/strong&gt; — задокументировали паттерн ошибки. Файл &lt;code&gt;forbidden_patterns.md&lt;/code&gt;, паттерн №8: «подмена инструмента». С примером до и после. Чтобы в следующий раз агент видел: «я уже пробовал так делать — это ошибка».&lt;p&gt;&lt;strong&gt;Второе&lt;/strong&gt; — добавили явное предупреждение в промпт:&lt;pre&gt;&lt;code&gt;ВАЖНО: для внешних инструментов (FTSE_CHINA_50, DXY, MSCI_INDIA и др.)&#xA;данные ТОЛЬКО из: FROM market_data WHERE instrument = &amp;#39;&amp;lt;название&amp;gt;&amp;#39;&#xA;Никогда не переименовывай колонки из других таблиц.&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Третье&lt;/strong&gt; — запустили чистый скан с нуля. 618 комбинаций: 10 инструментов, 7 новостных тем, 5 целевых индексов, 6 временных лагов. На этот раз — честно.&lt;hr&gt;&lt;h3&gt;Что нашли на самом деле&lt;/h3&gt;&lt;p&gt;После чистого скана — шесть реальных паттернов. Не шестьдесят семь процентов. Шесть.&lt;p&gt;Зато настоящих.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Сигнал&lt;th&gt;&lt;p align=left&gt;r&lt;th&gt;&lt;p align=left&gt;Лаг&lt;th&gt;&lt;p align=left&gt;Механизм&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;USD/RUB → MOEXFN&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;+0.758&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;14 дней&lt;td&gt;&lt;p align=left&gt;Девальвация → переоценка активов&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Brent → MOEXFN&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;−0.702&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;90 дней&lt;td&gt;&lt;p align=left&gt;Нефть → рубль → ставка → маржа банков&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Ключевая ставка → MOEXFN&lt;td&gt;&lt;p align=left&gt;+0.651&lt;td&gt;&lt;p align=left&gt;0 дней&lt;td&gt;&lt;p align=left&gt;Банки зарабатывают на высокой марже&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;MSCI_INDIA → MOEXFN&lt;td&gt;&lt;p align=left&gt;+0.631&lt;td&gt;&lt;p align=left&gt;0 дней&lt;td&gt;&lt;p align=left&gt;Глобальный аппетит к рискованным рынкам&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;SP500 → MOEXFN&lt;td&gt;&lt;p align=left&gt;+0.593&lt;td&gt;&lt;p align=left&gt;7 дней&lt;td&gt;&lt;p align=left&gt;Мировой риск-аппетит с недельным лагом&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Банковские новости → MOEXFN&lt;td&gt;&lt;p align=left&gt;+0.549&lt;td&gt;&lt;p align=left&gt;14 дней&lt;td&gt;&lt;p align=left&gt;Работает только при ставке &amp;lt; 15%&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Самый нетривиальный — второй в таблице. Давайте разберём его подробнее.&lt;hr&gt;&lt;h3&gt;Нефть предсказывает банки в минус&lt;/h3&gt;&lt;p&gt;Нефть выросла — а через три месяца банки упали. Звучит странно, правда?&lt;p&gt;Вот цепочка, которая это объясняет:&lt;pre&gt;&lt;code&gt;Нефть дорожает&#xA;  → нефтяная выручка поступает в страну&#xA;  → экспортёры продают валюту, рубль укрепляется&#xA;  → инфляционное давление снижается&#xA;  → Центральный банк получает возможность снизить ставку&#xA;  → банки зарабатывают меньше (ставка = их маржа)&#xA;  → инвесторы переоценивают банковский сектор вниз&#xA;                                        (лаг ~90 дней)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Данные это подтверждают:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/d14/5e4/eb5/d145e4eb58e014febfcea2dcccf83783.png alt=&#34;Brent и MOEXFN 2022–2025&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/d14/5e4/eb5/d145e4eb58e014febfcea2dcccf83783.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/d14/5e4/eb5/d145e4eb58e014febfcea2dcccf83783.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Brent и MOEXFN 2022–2025&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;em&gt;Синяя линия — Brent. Зелёная пунктирная — финансовый индекс MOEXFN. В 2022 году нефть на пике ($117) — банки на минимуме. Когда нефть начала падать — банки пошли вверх.&lt;/em&gt;&lt;p&gt;Особенно убедительно вот что: корреляция монотонно растёт с увеличением временно́го сдвига:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/e48/e17/33b/e48e1733bcf2f0b37c146937d53a6c12.png alt=&#34;Корреляция по лагам&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/e48/e17/33b/e48e1733bcf2f0b37c146937d53a6c12.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/e48/e17/33b/e48e1733bcf2f0b37c146937d53a6c12.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Корреляция по лагам&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;em&gt;Если бы это был случайный шум — корреляция была бы максимальной при нулевом лаге и убывала бы с расстоянием. Монотонный рост — признак настоящей структурной задержки.&lt;/em&gt;&lt;p&gt;Но вот здесь мы обязаны быть честными.&lt;hr&gt;&lt;h3&gt;Честная часть&lt;/h3&gt;&lt;p&gt;Когда мы посмотрели на данные год за годом — картина стала сложнее:&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Год&lt;th&gt;&lt;p align=left&gt;Корреляция (лаг 90 дней)&lt;th&gt;&lt;p align=left&gt;Что происходило&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2022&lt;td&gt;&lt;p align=left&gt;−0.574&lt;td&gt;&lt;p align=left&gt;Начало СВО, санкции, нефть на пике — сильный сигнал&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2023&lt;td&gt;&lt;p align=left&gt;−0.113&lt;td&gt;&lt;p align=left&gt;Ставка растёт — нефтяной сигнал почти исчез&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;−0.097&lt;td&gt;&lt;p align=left&gt;Ставка 21% — полностью заглушила нефть&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;+0.265&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Знак инвертировался&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Агрегатная корреляция −0.70 — во многом эффект одного аномального года. В 2023–2024 сигнал просто не работал.&lt;p&gt;Зато режимный анализ даёт ясный ответ:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/14e/129/b81/14e129b8186075ca92503503f1d6c322.png alt=&#34;Режимный анализ&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/14e/129/b81/14e129b8186075ca92503503f1d6c322.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/14e/129/b81/14e129b8186075ca92503503f1d6c322.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Режимный анализ&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Условие рынка&lt;th&gt;&lt;p align=left&gt;Корреляция&lt;th&gt;&lt;p align=left&gt;Что это значит&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Весь период 2022–2025&lt;td&gt;&lt;p align=left&gt;−0.702&lt;td&gt;&lt;p align=left&gt;✅ Реальный, структурный сигнал&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Сильный рубль (USD/RUB &amp;lt; 80)&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;−0.851&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;✅ Очень сильный&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Ставка ЦБ ≥ 16%&lt;td&gt;&lt;p align=left&gt;−0.100&lt;td&gt;&lt;p align=left&gt;❌ Сигнал не работает&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2025, ставка снижается&lt;td&gt;&lt;p align=left&gt;+0.265&lt;td&gt;&lt;p align=left&gt;⚠️ Знак сменился&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Главный вывод:&lt;/strong&gt; сигнал включается только в определённых условиях. При высокой ставке ЦБ — она перекрывает нефтяной сигнал и становится главным фактором.&lt;p&gt;Это не слабость результата. Это самое интересное открытие всего проекта.&lt;hr&gt;&lt;h3&gt;Главный урок&lt;/h3&gt;&lt;p&gt;Российский финансовый рынок — это рынок &lt;strong&gt;режимов&lt;/strong&gt;.&lt;p&gt;Один и тот же фактор работает по-разному при ставке 7% и при ставке 21%. При сильном рубле и при слабом. Нет одного универсального сигнала — есть сигналы, которые включаются и выключаются в зависимости от состояния системы.&lt;p&gt;Мы начинали с простого вопроса: «какие корреляции можно найти в данных?» А пришли к другому: «в каком режиме сейчас рынок — и какие сигналы в этом режиме работают?»&lt;p&gt;Это совсем другой вопрос. И он правильнее.&lt;hr&gt;&lt;h3&gt;Поиск истины&lt;/h3&gt;&lt;p&gt;Знаете, что меня больше всего радует в этом проекте?&lt;p&gt;Не шесть найденных сигналов. Не $5.76 за девять часов работы. Не 409 миллионов токенов.&lt;p&gt;Меня радует то, что мы нашли свою ошибку сами.&lt;p&gt;Никто нас не проверял. Никто не указывал пальцем. Мы получили красивые числа — и сами сказали: «подождите, это слишком хорошо». Запустили верификацию. Нашли баг. Написали об этом честно.&lt;p&gt;Именно так устроена настоящая наука. Не победное шествие от гипотезы к открытию. А спираль: предположение → проверка → сомнение → уточнение → новое предположение.&lt;p&gt;Ошибки — это не провал. Это самая ценная часть исследования. Потому что именно ошибки показывают, где на самом деле граница нашего понимания.&lt;p&gt;1943 итерации — это не результат. Это начало разговора.&lt;hr&gt;&lt;h3&gt;Что дальше&lt;/h3&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Шаг&lt;th&gt;&lt;p align=left&gt;Задача&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Phase 7&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Второй марафон — чистый агент, без старых ошибок&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Phase 8&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Обучение DeepSeek на нашем датасете (1953 примера)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Phase 9&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Режимный классификатор — агент сначала определяет состояние рынка, потом ищет сигналы&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Phase 9 — это тот самый качественный скачок. Научить алгоритм спрашивать «в каком режиме мы сейчас?» прежде чем искать сигналы — это другой уровень.&lt;hr&gt;&lt;h3&gt;Посмотреть самому&lt;/h3&gt;&lt;p&gt;Всё открыто: &lt;a href=https://github.com/iRatG/signal-mind rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;strong&gt;github.com/iRatG/signal-mind&lt;/strong&gt;&lt;/a&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Открыть&lt;th&gt;&lt;p align=left&gt;Что внутри&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://htmlpreview.github.io/?https://github.com/iRatG/signal-mind/blob/master/habr/sample/brent_moexfn_investigation.html rel=&#34;noopener noreferrer nofollow&#34;&gt;📊 Разбор сигнала Brent→MOEXFN&lt;/a&gt;&lt;td&gt;&lt;p align=left&gt;SQL, 6 интерактивных графиков, режимный анализ&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://htmlpreview.github.io/?https://github.com/iRatG/signal-mind/blob/master/analytics/report.html rel=&#34;noopener noreferrer nofollow&#34;&gt;📈 Отчёт ночи тишины&lt;/a&gt;&lt;td&gt;&lt;p align=left&gt;8 графиков — телеметрия 9-часового запуска&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;pre&gt;&lt;code class=bash&gt;# Попробовать самому — 10 итераций&#xA;python -m src.agent.agent 10&#xA;&#xA;# Ночной марафон (9 часов)&#xA;python -m src.agent.watchdog 32400&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;Вдохновлено проектом &lt;/em&gt;&lt;a href=https://joi-lab.github.io/ouroboros/ rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;em&gt;Ouroboros&lt;/em&gt;&lt;/a&gt;&lt;p&gt;&lt;em&gt;Python · DuckDB · ChromaDB · DeepSeek API · MOEX · ЦБ РФ · 2.52M новостей&lt;/em&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>AyratGil</author>
      <guid>https://habr.com/ru/articles/1030160/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030160</guid>
      <pubDate>Thu, 30 Apr 2026 13:26:18 +0000</pubDate>
    </item>
    <item>
      <title>[Перевод] System Design: проектируем сервис быстрых знакомств</title>
      <link>https://habr.com/ru/articles/1026316/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1026316</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Видеоразбор этой задачи на русском языке можно посмотреть здесь - &lt;a href=&#34;https://www.youtube.com/watch?v=U_qR7HFZIbE&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;https://www.youtube.com/watch?v=U_qR7HFZIbE&lt;/a&gt;&lt;hr&gt;&lt;h2&gt;Проектирование Tinder&lt;/h2&gt;&lt;h3&gt;Постановка задачи&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;❤️ Что такое Tinder?&lt;/strong&gt;&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Tinder - это мобильное приложение для знакомств. Пользователи видят профили друг друга и свайпают (смахивают, swipe) вправо, если профиль понравился, и влево - если нет. Приложение использует геоданные и пользовательские фильтры, чтобы показывать потенциальные матчи (совпадения, match) поблизости.&lt;/blockquote&gt;&lt;h4&gt;Функциональные требования&lt;/h4&gt;&lt;p&gt;Основные требования&lt;ol&gt;&lt;li&gt;&lt;p&gt;Пользователи могут создать профиль с предпочтениями (например, возрастной диапазон, интересы) и указать максимальную дистанцию.&lt;li&gt;&lt;p&gt;Пользователи могут просматривать список потенциальных матчей, соответствующих их предпочтениям и находящихся в пределах максимальной дистанции от текущей локации.&lt;li&gt;&lt;p&gt;Пользователи могут свайпать вправо/влево по одному профилю за раз, выражая “да” или “нет” по отношению к другим пользователям.&lt;li&gt;&lt;p&gt;Пользователи получают уведомление о матче, если они взаимно свайпнули друг друга вправо.&lt;/ol&gt;&lt;p&gt;За рамками задачи&lt;ul&gt;&lt;li&gt;&lt;p&gt;Пользователи могут загружать фотографии.&lt;li&gt;&lt;p&gt;Пользователи могут общаться в личных сообщениях после матча.&lt;li&gt;&lt;p&gt;Пользователи могут покупать и использовать другие premium функции.&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;Важно отметить, что в этой задаче обычно фокусируются на “ленте рекомендаций” и пользовательском опыте свайпов, а не на вспомогательных возможностях. Если вы не уверены, на каких функциях сфокусироваться для такого приложения, стоит выяснить у интервьюера, какая часть системы для него наиболее важна. Обычно это либо часть продукта, которая делает его уникальным, либо наиболее сложная часть продукта.&lt;/blockquote&gt;&lt;h4&gt;Нефункциональные требования&lt;/h4&gt;&lt;p&gt;Основные требования&lt;ol&gt;&lt;li&gt;&lt;p&gt;Система должна обеспечивать сильную согласованность (strong consistency) для свайпов. Если пользователь свайпнул “да” на человека, который уже свайпнул “да” на него, оба должны получить уведомление о матче.&lt;li&gt;&lt;p&gt;Система должна масштабироваться под большое количество ежедневных активных / одновременных пользователей (20 млн DAU, в среднем ~100 свайпов на пользователя в день).&lt;li&gt;&lt;p&gt;Система должна быстро формировать список потенциальных матчей (&amp;lt; 300 мс).&lt;li&gt;&lt;p&gt;Система не показывает повторно профили, по которым пользователь уже свайпал.&lt;/ol&gt;&lt;p&gt;За рамками задачи&lt;ul&gt;&lt;li&gt;&lt;p&gt;Система должна предотвращать использование фейковых профилей.&lt;/ul&gt;&lt;p&gt;На доске это могло бы выглядеть примерно так:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/49/43/74/49437488fd2cef638ae4d83f06169bf0.png alt=&#34;Нефункциональные требования&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/49/43/74/49437488fd2cef638ae4d83f06169bf0.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/49/43/74/49437488fd2cef638ae4d83f06169bf0.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Нефункциональные требования&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h3&gt;Подготовка&lt;/h3&gt;&lt;h4&gt;Планирование подхода&lt;/h4&gt;&lt;p&gt;Прежде чем переходить к проектированию системы, важно на секунду остановиться и продумать стратегию. К счастью, для “продуктовых” задач план обычно простой: последовательно собирать дизайн, проходя по функциональным требованиям одно за другим. Так вы сохраните фокус и не утонете в деталях.&lt;p&gt;Когда функциональные требования удовлетворены, используйте нефункциональные требования, чтобы определить направления для погружения в детали, где это необходимо.&lt;h4&gt;Проектирование API&lt;/h4&gt;&lt;p&gt;Начнем с определения основных сущностей, это поможет спроектировать API. Пока не обязательно знать каждое поле или колонку, но если у вас уже есть представление о том, что там будет, можно это записать.&lt;p&gt;Для Tinder основные сущности довольно очевидны:&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;User&lt;/strong&gt;: это и пользователь приложения, и профиль, который показывают другим пользователям.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Swipe&lt;/strong&gt;: выражение “да” или “нет” по отношению к профилю другого пользователя. Содержит информацию о пользователе, который свайпает (&lt;code&gt;swiping_user&lt;/code&gt;), и пользователе, которого свайпают (&lt;code&gt;target_user&lt;/code&gt;).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Match&lt;/strong&gt;: связь между двумя пользователями, возникающая в результате взаимного свайпа “да”.&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;В реальном интервью короткого списка, как выше, часто достаточно. Главное - проговорить сущности с интервьюером и убедиться, что вы оба одинаково их понимаете.&lt;/blockquote&gt;&lt;p&gt;API - основной интерфейс, через который пользователи взаимодействуют с системой. Его полезно определить с самого начала, поскольку он направляет high-level дизайн. Обычно нам нужен один эндпоинт на каждое функциональное требование.&lt;p&gt;Первый эндпоинт - создание/обновление профиля пользователя. Конечно, в реальном Tinder там будут фото, биография и т. п., но в этой задаче сфокусируемся на предпочтениях для поиска и сопоставления.&lt;pre&gt;&lt;code&gt;POST /profile&#xA;{&#xA;  min_age,&#xA;  max_age,&#xA;  max_distance,&#xA;  interested_in: &amp;#34;female&amp;#34; | &amp;#34;male&amp;#34;,&#xA;  ...&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Далее нужен эндпоинт, который возвращает “ленту” профилей потенциальных кандидатов.&lt;pre&gt;&lt;code&gt;GET /feed?lat={}&amp;amp;long={} -&amp;gt; User[]&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;Заметьте: нам не нужно передавать фильтры (возраст, интересы, дистанцию и т. п.), потому что мы считаем, что пользователь уже сохранил их в настройках - и мы можем подгрузить их на сервере. Текущая локация может постоянно меняться, поэтому мы передаем ее с клиента.&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Может возникнуть желание заранее продумать пагинацию для &lt;code&gt;GET /feed&lt;/code&gt;. Для Tinder это обычно избыточно, вместо формирования страниц, приложение просто вызовет эндпоинт еще раз, если текущий список потенциальных кандидатов закончился.&lt;/blockquote&gt;&lt;p&gt;Также нужен эндпоинт для свайпа:&lt;pre&gt;&lt;code&gt;POST /swipe/{userId}&#xA;{&#xA;  &amp;#34;decision&amp;#34;: &amp;#34;yes&amp;#34; | &amp;#34;no&amp;#34;&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;blockquote&gt;&lt;p&gt;В каждом из этих запросов информация о пользователе передается в заголовках (через session token или auth token). Это распространенный паттерн, так мы можем обеспечивать аутентификацию/авторизацию и безопасность. Не стоит передавать пользовательские данные в теле запроса: в этом случае их можно легко подделать.&lt;/blockquote&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/c6/7b/f2/c67bf27e2a40c514497780fc3a09c7f8.png alt=&#34;Проектирование API и сущностей&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/c6/7b/f2/c67bf27e2a40c514497780fc3a09c7f8.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/c6/7b/f2/c67bf27e2a40c514497780fc3a09c7f8.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Проектирование API и сущностей&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h3&gt;Высокоуровневый дизайн&lt;/h3&gt;&lt;h4&gt;1. Пользователи могут создать профиль с предпочтениями и указать максимальную дистанцию&lt;/h4&gt;&lt;p&gt;Первое, что нужно сделать в приложении знакомств - дать пользователям задать предпочтения, чтобы повысить шанс совпадений: показывать только те профили, которые подходят под эти предпочтения.&lt;p&gt;Мы принимаем запрос &lt;code&gt;POST /profile&lt;/code&gt; и сохраняем настройки в базе. Для старта нам достаточно простой архитектуры Клиент -&amp;gt; Сервер -&amp;gt; База данных. При этом, если сразу очевидно, что мы будем использовать несколько сервисов, то можно сразу добавить на схему API Gateway для маршрутизации запросов.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/95/1b/0f/951b0f5b7d6d2bc9970a258494577b62.png alt=&#34;Создание профиля с предпочтениями&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/95/1b/0f/951b0f5b7d6d2bc9970a258494577b62.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/95/1b/0f/951b0f5b7d6d2bc9970a258494577b62.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Создание профиля с предпочтениями&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Клиент&lt;/strong&gt;: пользователи взаимодействуют с системой через мобильное приложение.&lt;li&gt;&lt;p&gt;&lt;strong&gt;API Gateway&lt;/strong&gt;: маршрутизирует запросы в нужные сервисы. В данном случае - в сервис профилей.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Сервис профилей&lt;/strong&gt;: обрабатывает запросы на профили, обновляя предпочтения в базе.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Хранилище профилей&lt;/strong&gt;: хранит информацию о профилях, предпочтениях и другие релевантные данные.&lt;/ol&gt;&lt;p&gt;Когда пользователь создает профиль:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Клиент отправляет &lt;code&gt;POST /profile&lt;/code&gt; с данными профиля в теле запроса.&lt;li&gt;&lt;p&gt;API Gateway направляет запрос в сервис профилей.&lt;li&gt;&lt;p&gt;Сервис профилей обновляет предпочтения пользователя в базе.&lt;li&gt;&lt;p&gt;Результат возвращается клиенту.&lt;/ol&gt;&lt;h4&gt;2. Пользователи могут просматривать список потенциальных матчей&lt;/h4&gt;&lt;p&gt;Когда пользователь открывает приложение, он сразу видит список профилей для свайпа. Эти профили должны соответствовать фильтрам (возраст, интересы и т. п.), а также географии пользователя (например “&amp;lt; 2 км”, “&amp;lt; 5 км”, “&amp;lt; 15 км”).&lt;p&gt;Эффективная выдача этого списка - одна из самых сложных и интересных задач для этого приложения, но мы начнем с простой реализации и позже оптимизируем ее в детальном погружении.&lt;p&gt;Самое простое - запросить в базе пользователей, подходящих под фильтры, и вернуть их клиенту. Нам также важно учесть текущую локацию пользователя, чтобы показывать только ближайших кандидатов.&lt;p&gt;Простейший запрос мог бы выглядеть так:&lt;pre&gt;&lt;code class=sql&gt;SELECT * FROM users&#xA;WHERE age BETWEEN preferredAgeMin AND preferredAgeMax&#xA;AND gender = preferredInterestedIn&#xA;AND lat BETWEEN userLat - preferredDistance AND userLat + preferredDistance&#xA;AND long BETWEEN userLong - preferredDistance AND userLong + preferredDistance&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/9f/ce/57/9fce57fa31efe2afa4e7ac6733fbd687.png alt=&#34;Список потенциальных матчей&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/9f/ce/57/9fce57fa31efe2afa4e7ac6733fbd687.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/9f/ce/57/9fce57fa31efe2afa4e7ac6733fbd687.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Список потенциальных матчей&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Когда пользователь запрашивает новый набор профилей:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Клиент отправляет &lt;code&gt;GET /feed&lt;/code&gt;, передавая текущую локацию через query‑параметры.&lt;li&gt;&lt;p&gt;API Gateway направляет запрос в сервис профилей.&lt;li&gt;&lt;p&gt;Сервис профилей запрашивает базу данных, выбирая пользователей по предпочтениям и локации.&lt;li&gt;&lt;p&gt;Результаты возвращаются клиенту.&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;Если вы читали другие разборы, то знаете, что такой запрос будет неэффективным. В частности, поиск по локации, даже с использованием базовых индексов, будет очень медленным. Когда мы будем погружаться в детали, нам придется применить более продвинутые подходы к индексации и запросам.&lt;/blockquote&gt;&lt;h4&gt;3. Пользователи могут свайпать вправо/влево и выражать да/нет по отношению к другим пользователям&lt;/h4&gt;&lt;p&gt;Когда пользователи получили список профилей, они готовы свайпать. Система должна записывать каждый свайп и сообщать пользователю о матче, если тот, кому он свайпнул “да”, уже ранее свайпнул “да” пользовательский профиль.&lt;p&gt;Нам нужен способ сохранять свайпы и проверять, произошел ли матч. Снова начнем с простого (и неидеального) решения и улучшим его в детальном погружении.&lt;p&gt;Добавим два новых компонента:&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Сервис свайпов&lt;/strong&gt;: сохраняет свайпы и проверяет матчи.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Хранилище свайпов&lt;/strong&gt;: хранит данные свайпов.&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;Почему мы выбрали отдельный сервис и отдельное хранилище?&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Обоснование: создание/обновление профиля происходят существенно реже, чем запись свайпов. Разделив сервисы, мы можем независимо масштабировать сервис свайпов. Аналогично, по данным, свайпов будет очень много. При &lt;code&gt;20 млн DAU × 100 свайпов/день × ~100 байт на свайп&lt;/code&gt; получается порядка &lt;strong&gt;~200GB данных в день&lt;/strong&gt;. Такой объем и нагрузку хорошо сможет обработать хранилище оптимизированное для записи такое как Cassandra (которое может быть не лучшим выбором для профилей). Кроме того, отдельное хранилище позволяет оптимизировать паттерны доступа и кэширование для свайпов без влияния на сервис профилей.&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;Такое разделение - не универсальный ответ для всех систем. Но здесь плюсы перевешивают минусы.&lt;/blockquote&gt;&lt;p&gt;Поскольку свайп - действие почти без усилий, можно ожидать большой поток записей. Если принять &lt;code&gt;20 млн DAU&lt;/code&gt; и в среднем &lt;code&gt;100&lt;/code&gt; свайпов/день, это &lt;code&gt;&lt;strong&gt;2 млрд&lt;/strong&gt;&lt;/code&gt;&lt;strong&gt; записей в день&lt;/strong&gt;. Это почти наверняка означает, что данные нужно партиционировать.&lt;p&gt;Cassandra хорошо подходит как хранилище свайпов. Мы можем партиционировать по &lt;code&gt;swiping_user_id&lt;/code&gt;. Тогда проверка “свайпал ли пользователь A пользователя B” будет быстрой: мы предсказуемо идем в один раздел (partition). Также Cassandra хорошо выдерживает большие объемы записей благодаря архитектуре своего хранения (CommitLog + Memtables + SSTables). Недостатком использования Cassandra здесь является конечная согласованность для данных о свайпах. Мы обсудим способы нивелировать этот недостаток когда будем погружаться в детали.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/94/a5/f9/94a5f9c049cbafb455647f4d70e3ae02.png alt=&#34;Свайп вправо/влево и запись результата&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/94/a5/f9/94a5f9c049cbafb455647f4d70e3ae02.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/94/a5/f9/94a5f9c049cbafb455647f4d70e3ae02.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Свайп вправо/влево и запись результата&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Когда пользователь свайпает:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Клиент отправляет &lt;code&gt;POST /swipe/{userId}&lt;/code&gt; с &lt;code&gt;userId&lt;/code&gt; профиля и результатом свайпа (вправо/влево).&lt;li&gt;&lt;p&gt;API Gateway направляет запрос в сервис свайпов.&lt;li&gt;&lt;p&gt;Сервис свайпов записывает свайп в хранилище свайпов.&lt;li&gt;&lt;p&gt;Сервис свайпов проверяет наличие обратного свайпа и, если он есть, сообщает клиенту о матче.&lt;/ol&gt;&lt;h4&gt;4. Пользователи получают уведомление о матче при взаимном свайпе&lt;/h4&gt;&lt;p&gt;При матче нужно уведомить обоих людей. Чтобы было понятнее, назовем первого, кто лайкнул, Алиса, а второго - Боб.&lt;p&gt;Уведомить Боба просто: это мы уже делаем. Поскольку он - второй, сразу после свайпа вправо мы проверяем, лайкнула ли Алиса, и если да - показываем уведомление на устройстве Боба.&lt;p&gt;Но что насчет Алисы? Она могла свайпнуть Боба несколькими днями ранее. Нам нужно отправить push‑уведомление на устройство Алисы, сообщив, что у нее новый матч.&lt;p&gt;Для этого будем использовать нативные сервисы push-уведомлений, такие как Apple Push Notification Service (APNS) или Firebase Cloud Messaging (FCM).&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/e9/a6/44/e9a644483fae7c9489869063b78cc9b5.png alt=&#34;Уведомление о матче&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/e9/a6/44/e9a644483fae7c9489869063b78cc9b5.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/e9/a6/44/e9a644483fae7c9489869063b78cc9b5.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Уведомление о матче&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;blockquote&gt;&lt;p&gt;APNS и FCM - это службы push‑уведомлений, с собственным набором API и SDK, которые мы можем использовать для отправки push‑уведомлений на пользовательские устройства.&lt;/blockquote&gt;&lt;p&gt;Кратко повторим полный процесс свайпа с учетом пушей:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Некоторое время назад Алиса свайпнула вправо Боба, и мы сохранили это в хранилище свайпов.&lt;li&gt;&lt;p&gt;Боб свайпает вправо Алису.&lt;li&gt;&lt;p&gt;Сервер проверяет наличие обратного свайпа и находит его.&lt;li&gt;&lt;p&gt;Мы показываем уведомление о матче на устройстве Боба сразу после свайпа.&lt;li&gt;&lt;p&gt;Мы отправляем push‑уведомление через APNS/FCM Алисе, что у нее новый матч.&lt;/ol&gt;&lt;blockquote&gt;&lt;p&gt;Исходя из функциональных требований, мы не должны заботиться о том, что происходит после матча, поэтому мы можем не углубляться в детали хранения матчей. Также можно предположить, что за доставку push‑уведомлений отвечает внешний сервис. Важно проговаривать такие допущения с интервьюером.&lt;/blockquote&gt;&lt;h3&gt;Потенциальные погружения в детали&lt;/h3&gt;&lt;p&gt;К этому моменту у нас есть базовая работающая система, удовлетворяющая функциональным требованиям. Но есть несколько областей, куда полезно углубиться, чтобы улучшить производительность, масштабируемость и т.д. В зависимости от вашего уровня, от вас ожидается, что вы будете направлять дискуссию на эти темы, представляющие наибольший интерес.&lt;h4&gt;1. Как обеспечить согласованность и низкую задержку при свайпах?&lt;/h4&gt;&lt;p&gt;Рассмотрим проблемный сценарий. Представим, что Алиса и Боб почти одновременно свайпают друг друга вправо. Порядок операций может оказаться примерно таким:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Свайп Алисы приходит на сервер - мы проверяем обратный свайп. Его нет.&lt;li&gt;&lt;p&gt;Свайп Боба приходит на сервер - мы проверяем обратный свайп. Его нет.&lt;li&gt;&lt;p&gt;Мы сохраняем свайп Алисы на Боба.&lt;li&gt;&lt;p&gt;Мы сохраняем свайп Боба на Алису.&lt;/ol&gt;&lt;p&gt;В итоге свайпы сохранены, но мы упустили момент создания матча и уведомления. Оба человека будут жить дальше, не зная о совпадении - настоящая любовь так и не случится. Мы не можем этого допустить!&lt;blockquote&gt;&lt;p&gt;Стоит заметить: можно решить эту проблему и без сильной согласованности. Например, сделать отдельный процесс согласования (reconciliation), который периодически пробегает по свайпам и создает матчи там, где они должны были появиться. Для таких случаев можно отправить уведомления обоим пользователям. Оба пользователя решат, что второй человек свайпнул их прямо сейчас. Это позволило бы приоритизировать доступность перед согласованностью и было бы интересным компромиссом для обсуждения. Однако, задача собеседования станет менее сложной, и, в целях оценки ваших навыков, интервьюер может предложить все же спроектировать решение, которое приоритизирует согласованность.&lt;/blockquote&gt;&lt;p&gt;Раз нам нужно уведомлять последнего пользователя из пары незамедлительно, система должна быть согласованной. Рассмотрим несколько подходов.&lt;details class=spoiler&gt;&lt;summary&gt;Хорошее решение: Транзакции&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Если нужна согласованность, первое, о чем стоит подумать, это транзакции в базе данных. Мы можем сделать так, чтобы и запись свайпа, и проверка обратного свайпа выполнялись в одной транзакции: либо атомарно делается все, либо ничего.&lt;p&gt;В Cassandra есть базовая поддержка того, что они называют легковесные транзакции (Lightweight Transactions, LWT), но это не полноценные ACID‑транзакции. LWT используют консенсусный протокол Paxos для обеспечения линеаризуемой согласованности (linearizable consistency) для конкретных операций, но только в рамках &lt;strong&gt;одного раздела (partition)&lt;/strong&gt;. Между разделами они не дают атомарности, уровней изоляции и откатов. Также LWT имеют существенные накладные расходы из‑за нескольких сетевых коммуникаций (round-trips) между нодами. Это делает такие транзакции подходящими в основном для простых условных обновлений.&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Главная проблема при этом подходе - масштабируемость. &lt;strong&gt;2 млрд свайпов в день&lt;/strong&gt; не поместятся в один раздел, а транзакции на несколько разделов LWT не поддерживают.&lt;p&gt;В следующем решении обсудим, как добиться того, чтобы взаимные свайпы всегда находились в одном разделе.&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Отличное решение: Транзакции в одном разделе&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Можно использовать LWT транзакции Cassandra, атомарно обрабатывая свайпы. Ключевая идея - гарантировать, что все свайпы между двумя пользователями попадают в один раздел.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Сперва создадим таблицу с составным ключом, который группирует пару пользователей:&lt;/ol&gt;&lt;pre&gt;&lt;code class=sql&gt;CREATE TABLE swipes (&#xA;    user_pair text,      -- ключ партиционирования: smaller_id:larger_id&#xA;    from_user uuid,      -- кластерный ключ&#xA;    to_user uuid,        -- кластерный ключ&#xA;    direction text,&#xA;    created_at timestamp,&#xA;    PRIMARY KEY ((user_pair), from_user, to_user)&#xA;);&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol start=2&gt;&lt;li&gt;&lt;p&gt;При свайпе формируем &lt;code&gt;user_pair&lt;/code&gt;, отсортировав id, для обеспечения согласованности:&lt;/ol&gt;&lt;pre&gt;&lt;code class=python&gt;def get_user_pair(user_a, user_b):&#xA;    # Отсортируем id так, чтобы (A-&amp;gt;B) и (B-&amp;gt;A) были в одном разделе&#xA;    sorted_ids = sorted([user_a, user_b])&#xA;    return f&amp;#34;{sorted_ids[0]}:{sorted_ids[1]}&amp;#34;&#xA;&#xA;def handle_swipe(from_user, to_user, direction):&#xA;    user_pair = get_user_pair(from_user, to_user)&#xA;&#xA;    # Обе операции выполняются атомарно в одном разделе&#xA;    batch = &amp;#34;&amp;#34;&amp;#34;&#xA;    BEGIN BATCH&#xA;        INSERT INTO swipes (user_pair, from_user, to_user, direction, created_at)&#xA;        VALUES (?, ?, ?, ?, ?);&#xA;&#xA;        SELECT direction FROM swipes&#xA;        WHERE user_pair = ?&#xA;        AND from_user = ?&#xA;        AND to_user = ?;&#xA;    APPLY BATCH;&#xA;    &amp;#34;&amp;#34;&amp;#34;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Этот подход эффективен, поскольку транзакции Cassandra в одном разделе обеспечивают необходимые нам гарантии атомарности. Обеспечивая хранение всех операций между двумя пользователями в одном разделе, мы можем атомарно проверять совпадения, что делает решение одновременно производительным и надежным.&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Хотя это решение элегантно реализует необходимую функциональность, оно создает некоторые эксплуатационные проблемы. По мере того, как пары пользователей со временем накапливают историю, размеры разделов могут значительно вырасти, что потенциально влияет на производительность. Кроме того, высокоактивные пользователи могут создавать “горячие” разделы, получающие непропорционально большой объем трафика. Чтобы решить эти проблемы, нам нужна надежная стратегия архивирования или удаления старых данных свайпов, предотвращающая неограниченный рост разделов и сохраняющая при этом важные исторические данные.&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Отличное решение: Redis для атомарных операций&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Redis еще лучше подходит для согласованности, которая нужна в логике матчинга по свайпам. Cassandra хороша для надежного хранения огромных объемов данных, но не так хорошо при исполнении атомарных операций, которые нужны для обнаружения матча в реальном времени. Поэтому можно использовать Redis для атомарной обработки свайпов, а Cassandra - как надежный слой хранения.&lt;p&gt;Ключевая идея та же: свайпы между одной и той же парой пользователей должны попадать на один шард Redis. Можно добиться этого, формируя ключ из двух user_id в детерминированном порядке.&lt;p&gt;Структура ключа и значения:&lt;pre&gt;&lt;code&gt;Key: &amp;#34;swipes:123:456&amp;#34;&#xA;Value: {&#xA;  &amp;#34;123_swipe&amp;#34;: &amp;#34;yes&amp;#34;,&#xA;  &amp;#34;456_swipe&amp;#34;: &amp;#34;no&amp;#34;&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=python&gt;def get_key(user_a, user_b):&#xA;    # Отсортируем id так, чтобы (A-&amp;gt;B) и (B-&amp;gt;A) были в одном разделе&#xA;    sorted_ids = sorted([user_a, user_b])&#xA;    return f&amp;#34;swipes:{sorted_ids[0]}:{sorted_ids[1]}&amp;#34;&#xA;&#xA;def handle_swipe(from_user, to_user, direction):&#xA;    key = get_key(from_user, to_user)&#xA;&#xA;    # Используем Redis хеш для хранения свайпов обоих пользователей&#xA;    # Хеш имеет два поля: user1_swipe и user2_swipe&#xA;    script = &amp;#34;&amp;#34;&amp;#34;&#xA;    redis.call(&amp;#39;HSET&amp;#39;, KEYS[1], ARGV[1], ARGV[2])&#xA;    return redis.call(&amp;#39;HGET&amp;#39;, KEYS[1], ARGV[3])&#xA;    &amp;#34;&amp;#34;&amp;#34;&#xA;&#xA;    # Выполняем атомарно используя Lua скрипт&#xA;    other_swipe = redis.eval(&#xA;        script,&#xA;        keys=[key],&#xA;        args=[&#xA;            f&amp;#34;{from_user}_swipe&amp;#34;,  # поле для установки&#xA;            direction,             # наш свайп&#xA;            f&amp;#34;{to_user}_swipe&amp;#34;     # поле для проверки&#xA;        ]&#xA;    ) # Возвращает значение другого пользователя&#xA;&#xA;    # Если другой пользователь тоже свайпнул вправо, это матч!&#xA;    if direction == &amp;#39;right&amp;#39; and other_swipe == &amp;#39;right&amp;#39;:&#xA;        create_match(from_user, to_user)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Используя атомарные операции Redis через Lua скрипт, мы можем гарантировать, что запись свайпа и проверка совпадения выполняются как одна операция. Это дает нам необходимую согласованность и высокую производительность благодаря тому, что Redis работает в памяти. Система масштабируется горизонтально, поскольку мы можем добавлять больше узлов Redis, а согласованное хеширование гарантирует, что связанные свайпы остаются вместе.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/d7/38/7f/d7387f159b81974dd3e3c81cfe39bfa5.png alt=&#34;Redis для атомарных операций&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/d7/38/7f/d7387f159b81974dd3e3c81cfe39bfa5.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/d7/38/7f/d7387f159b81974dd3e3c81cfe39bfa5.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Redis для атомарных операций&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Основная проблема при таком подходе - эффективное управление кластером Redis. Несмотря на то, что Redis обеспечивает отличную производительность для атомарных операций, нам необходимо обрабатывать сбои узлов и выполнять повторную балансировку кольца согласованного хеширования. Однако, обычно решение этих операционных проблем проще, чем попытки добиться согласованности в Cassandra.&lt;p&gt;Управление памятью - еще один фактор, который следует учитывать, но поскольку мы используем Cassandra в качестве надежного уровня хранения, мы можем быть агрессивными в отношении истечения срока хранения данных из Redis. Мы можем периодически сбрасывать данные свайпов из Redis в Cassandra и сохранять в Redis только недавние свайпы. Если мы когда-либо потеряем данные Redis из-за сбоя узла, мы потеряем только возможность обнаруживать матчи для самых недавних свайпов - пользователи всегда могут свайпнуть снова, и мы не теряем историческую запись в Cassandra.&lt;p&gt;Этот гибридный подход дает нам лучшее из обоих инструментов: строгую согласованность и атомарные операции Redis для обнаружения матчей в реальном времени в сочетании с надежностью Cassandra и возможностями хранения исторических данных. Система остается высокодоступной и масштабируемой, одновременно удовлетворяя нашему основному требованию согласованности и немедленного обнаружения матчей.&lt;/div&gt;&lt;/details&gt;&lt;h4&gt;2. Как обеспечить быструю загрузку списка потенциальных матчей?&lt;/h4&gt;&lt;p&gt;Когда пользователь открывает приложение, он хочет начать свайпать сразу. Он не хочет ждать, пока мы построим ему список потенциальных матчей.&lt;p&gt;В высокоуровневом дизайне мы делали медленный запрос каждый раз, когда нужно сгенерировать новый список:&lt;pre&gt;&lt;code class=sql&gt;SELECT * FROM users&#xA;WHERE age BETWEEN preferredAgeMin AND preferredAgeMax&#xA;AND gender = preferredInterestedIn&#xA;AND lat BETWEEN userLat - preferredDistance AND userLat + preferredDistance&#xA;AND long BETWEEN userLong - preferredDistance AND userLong + preferredDistance&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Очевидно, это не удовлетворит требованию быстрой загрузки. Посмотрим, что можно сделать.&lt;details class=spoiler&gt;&lt;summary&gt;Хорошее решение: Индексированные БД для real-time запросов&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Один из способов добиться низкой задержки - использовать индексированные БД для запросов реального времени. Если создать индексы по полям, используемым при построении списка (предпочтения, возраст, и особенно geo‑данные), можно сильно ускорить время ответа. Геопространственный индекс позволяет эффективно находить пользователей в заданной области.&lt;p&gt;Для масштабируемости и требований Tinder можно использовать БД оптимизированную для поиска вроде Elasticsearch или OpenSearch. Они заточены под быстрый поиск и сложные запросы, что делает их пригодными для обработки больших объемов данных с минимальной задержкой.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/fa/dd/6a/fadd6a465dcbf9ab950390a36d3f6bc1.png alt=&#34;Индексированные БД для запросов списка потенциальных матчей&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/fa/dd/6a/fadd6a465dcbf9ab950390a36d3f6bc1.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/fa/dd/6a/fadd6a465dcbf9ab950390a36d3f6bc1.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Индексированные БД для запросов списка потенциальных матчей&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Главная сложность - поддержание согласованности между основной транзакционной БД и поисковым индексом. Любая задержка или сбой синхронизации приведут к тому, что пользователь увидит устаревшие профили или пропустит новые потенциальные матчи.&lt;p&gt;Обычно это решают через механизм Change Data Capture (CDC). Это шаблон, который фиксирует изменения базы данных (вставки, обновления, удаления) и передает их в другие системы. Часто реализуется путем мониторинга журнала упреждающей записи (write-ahead log) базы данных.&lt;p&gt;В зависимости от скорости обновлений, нам также может понадобиться стратегия пакетной обработки, чтобы уменьшить количество операций записи в индексированную базу данных, поскольку Elasticsearch оптимизирован для нагрузок с интенсивным чтением, а не интенсивной записью.&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Хорошее решение: Предварительные вычисления и кэширование&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Другой подход - асинхронно предвычислять (pre‑compute) и кешировать списки потенциальных матчей. Периодические фоновые cron задания формируют списки по предпочтениям и локации и кладут их в кэш, чтобы при открытии приложения выдавать мгновенно - без вычислений.&lt;p&gt;Такой кэшированный список дает пользователю моментальный доступ к профилям, улучшая UX. Предвычисления можно делать в “непиковое” время, а сокращение частоты запуска cron задания помогут держать списки потенциальных матчей актуальными.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/9a/7c/35/9a7c35fbc437e7ba0046c367b668945c.png alt=&#34;Предварительные вычисления и кэширование&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/9a/7c/35/9a7c35fbc437e7ba0046c367b668945c.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/9a/7c/35/9a7c35fbc437e7ba0046c367b668945c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Предварительные вычисления и кэширование&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Очень активные пользователи быстро “съедят” кэшированные списки, и затем придется подгружать/генерировать новые списки - это медленно и неэффективно.&lt;p&gt;Кроме того, предвычисленные списки могут не учитывать последние изменения профилей, предпочтений, появление новых пользователей - качество потенциальных матчей падает.&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Отличное решение: Комбинация предвычислений и индексированной БД&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Хорошая новость в том, что мы можем получить лучшее из обоих решений, объединив преимущества как предварительных вычислений, так и запросов в реальном времени с использованием индексированной базы данных. Мы периодически предварительно вычисляем и кэшируем списки потенциальных матчей для пользователей на основе их предпочтений и местоположения.&lt;p&gt;Когда пользователь открывает приложение, он мгновенно получает этот кэшированный список, что позволяет немедленно взаимодействовать без каких-либо задержек. Когда пользователи пролистывают и потенциально исчерпывают свой кэшированный список, система плавно переходит к генерации дополнительных потенциальных матчей в режиме реального времени. Это достигается за счет использования Elasticsearch индексированной базы данных, которую мы обсуждали выше.&lt;p&gt;Комбинируя эти два метода, мы поддерживаем низкую задержку на протяжении всего сеанса пользователя. Первоначальный кэшированный список обеспечивает мгновенный доступ, а индексированная база данных гарантирует, что даже самые активные пользователи получат свежие и актуальные потенциальные матчи без заметных задержек. Мы также можем инициировать обновление списка, когда пользователю осталось просмотреть несколько профилей, чтобы с точки зрения пользователя список казался бесконечным.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/6e/91/61/6e916123f007b9e99f46264c3d9e248e.png alt=&#34;Комбинация: предварительные вычисления + индексированная база данных&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/6e/91/61/6e916123f007b9e99f46264c3d9e248e.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/6e/91/61/6e916123f007b9e99f46264c3d9e248e.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Комбинация: предварительные вычисления + индексированная база данных&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Как мы можем решить проблему устаревших списков?&lt;/strong&gt;&lt;p&gt;Кэширование списков пользователей может привести к тому, что мы будем предлагать “устаревшие” профили:&lt;ul&gt;&lt;li&gt;&lt;p&gt;пользователь, предложенный в списке, возможно, изменил местоположение и больше не находится достаточно близко, чтобы соответствовать критериям фильтра&lt;li&gt;&lt;p&gt;пользователь, предложенный в ленте, может изменить свой профиль (например, интересы) и больше не соответствует критериям фильтра&lt;/ul&gt;&lt;p&gt;Чтобы смягчить эту проблему, можно:&lt;ul&gt;&lt;li&gt;&lt;p&gt;поставить строгий TTL на кэш списков (например, &amp;lt; 1 часа) и пересчитывать по расписанию&lt;li&gt;&lt;p&gt;предвычислять списки только для действительно активных пользователей, а не для всех&lt;/ul&gt;&lt;p&gt;Преимущество заключается в том, что можно тюнить несколько параметров: TTL для кэшированных профилей, количество кэшированных профилей, набор пользователей, для которых мы кэшируем списки, и т. д.&lt;blockquote&gt;&lt;p&gt;Когда система имеет параметры, которые можно тюнить без изменения логики, это упрощает эксплуатацию. Параметры можно изменить, чтобы найти эффективную конфигурацию для масштаба/варианта использования системы, и корректировать ее с течением времени без необходимости изменения системы.&lt;/blockquote&gt;&lt;p&gt;Также есть вполне определенные пользовательские действия, которые делают кэш устаревшим:&lt;ul&gt;&lt;li&gt;&lt;p&gt;пользователь поменял фильтры - профили в кэше больше не релевантны&lt;li&gt;&lt;p&gt;пользователь значительно изменил местоположение (например, переехал в другой район/город) - кэш больше не соответствует фильтру “поблизости”&lt;/ul&gt;&lt;p&gt;Такие события могут запускать фоновое обновление списков, для поддержания их актуальности.&lt;/div&gt;&lt;/details&gt;&lt;h4&gt;3. Как избежать повторного показа профилей, по которым пользователь уже свайпал?&lt;/h4&gt;&lt;p&gt;Было бы довольно неприятно, если бы пользователям повторно показывали профили, которые они пролистнули. У пользователя может сложиться впечатление, что его свайпы вправо (“да”) не были записаны, или это может раздражать пользователей, когда они снова видят людей, которых они раннее свайпнули влево (“нет”). Мы должны разработать решение, которое исключит этот неприятный пользовательский опыт.&lt;details class=spoiler&gt;&lt;summary&gt;Плохое решение: Запрос в хранилище свайпов&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Сервис построения списка может запросить хранилище свайпов и сделать проверку на наличие свайпов пользователя, чтобы отфильтровать профили, по которым уже свайпали. Запрос “все свайпы пользователя” будет эффективным, потому что попадет в один раздел по &lt;code&gt;swiping_user_id&lt;/code&gt;.&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;С этим подходом есть две проблемы:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Если система предпочитает доступность над согласованностью, часть свайпов могла не попасть на все реплики - мы рискуем “пропустить” свайпы и повторно показать профиль.&lt;li&gt;&lt;p&gt;Если у пользователя огромная история свайпов, вернется много id, и проверка на наличие свайпов становится все более дорогой.&lt;/ol&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Отличное решение: Кэш + запрос в хранилище свайпов&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;p&gt;Развивая предыдущий подход, можно добавить кэш последних свайпов, чтобы уменьшить проблемы ориентированной на доступность системы. Однако, этот кэш мы можем хранить не на бэкенде, а на клиенте.&lt;p&gt;Держать кэш на сервере только чтобы пережить задержки репликации дорого и накладывает операционные издержки. Мы можем воспользоваться тем фактом, что клиент является частью системы, и хранить в нем данные последних свайпов. Это позволит клиенту отфильтровывать профили, которые могут быть показаны.&lt;p&gt;Этот клиентский кэш особенно полезен, когда пользователь быстро исчерпывает предвычисленный список. Представьте, что пользователь свайпает 200 заранее подгруженных профилей. Примерно на ~150 профиле клиент может:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Сделать запрос к серверу, чтобы начать генерацию нового списка.&lt;li&gt;&lt;p&gt;Получить новый список, когда он готов.&lt;li&gt;&lt;p&gt;Отфильтровать из нового списка те профили, по которым пользователь уже успел свайпнуть, пока шла генерация.&lt;/ol&gt;&lt;p&gt;Клиент работает как часть системы, поскольку мы можем предположить, что пользователь использует приложение только на одном устройстве. Следовательно, мы можем использовать клиент как место для хранения и управления данными.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/e7/d8/84/e7d884e11a0036cc8465b40f5ef30a07.png alt=&#34;Кэш + запрос в хранилище свайпов&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/e7/d8/84/e7d884e11a0036cc8465b40f5ef30a07.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/e7/d8/84/e7d884e11a0036cc8465b40f5ef30a07.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Кэш + запрос в хранилище свайпов&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Остается проблема пользователей с очень большой историей свайпов: проверка на &lt;code&gt;contains&lt;/code&gt; по большому множеству &lt;code&gt;id&lt;/code&gt; будет все медленнее по мере роста истории.&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Отличное решение: Кэш + запрос в хранилище свайпов + фильтр Блума&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;strong&gt;Подход&lt;/strong&gt;&lt;blockquote&gt;&lt;p&gt;Этот подход может выглядеть слегка “over-engineered”, но это вполне разумный сценарий для фильтра Блума: поддержать построение списка для пользователей с огромной историей свайпов.&lt;/blockquote&gt;&lt;p&gt;Если история свайпов превышает определенный порог (когда проверка на &lt;code&gt;contains&lt;/code&gt; становится дорогой), мы строим и кэшируем фильтр Блума и используем его для фильтрации.&lt;p&gt;Фильтр Блума иногда дает ложноположительные результаты (false positives) (например, что профиль уже свайпали, хотя это не так), но &lt;strong&gt;никогда&lt;/strong&gt; не дает ложноотрицательные (false negatives) (например, что профиль не свайпали, если его свайпали). Значит мы точно избегаем повторных показов, но можем не показать пользователю небольшое количество профилей из-за ложноположительных результатов.&lt;p&gt;Фильтр Блума обычно имеет настраиваемый процент ошибки, связанный с размером фильтра (сколько памяти он занимает), так что можно подобрать компромисс между числом ложноположительных результатов, потреблением памяти и скоростью фильтрации.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/85/95/4e/85954eb3b69df58ef87d1e84f4e13d13.png alt=&#34;Кэш + запрос в хранилище свайпов + фильтр Блума&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/85/95/4e/85954eb3b69df58ef87d1e84f4e13d13.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/85/95/4e/85954eb3b69df58ef87d1e84f4e13d13.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Кэш + запрос в хранилище свайпов + фильтр Блума&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Проблемы&lt;/strong&gt;&lt;p&gt;Основная сложность - управление кэшем фильтров Блума: их нужно обновлять и восстанавливать при отказах. Фильтр Блума легко пересобрать из данных свайпов, но на больших объемах данных пересборка при падении узла может быть дорогостоящей операцией.&lt;/div&gt;&lt;/details&gt;&lt;h3&gt;Что ожидается на каждом уровне?&lt;/h3&gt;&lt;p&gt;Хорошо, мы обсудили много всего. Возникает резонный вопрос: “сколько из этого реально ожидается от меня на интервью?”. Разберем по уровням.&lt;h4&gt;Middle&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Ширина vs глубина&lt;/strong&gt;: от Middle кандидата чаще ожидается ширина кругозора и знаний (примерно 80% vs 20%). Вы должны собрать понятный высокоуровневый дизайн, закрывающий все функциональные требования, но многие компоненты могут оставаться абстракциями, которые вы проработали и обсудили с интервьюером на поверхностном уровне.&lt;p&gt;&lt;strong&gt;Проверка базовых знаний&lt;/strong&gt;: интервьюер будет прощупывать базу, чтобы удостовериться, что вы понимаете, что делает каждый компонент. Например, добавив API Gateway, ожидайте вопрос “что он делает” и “как работает”.&lt;p&gt;&lt;strong&gt;Смешанный формат ведения&lt;/strong&gt;: вы должны уверенно вести ранние стадии интервью, но не обязательно проактивно находить все проблемы дизайна. Нормально, если позже интервьюер будет вести обсуждение, задавая вопросы и ставя дополнительные задачи.&lt;p&gt;&lt;strong&gt;Задача Tinder&lt;/strong&gt;: от Middle кандидата ожидается четко определенный API и модель данных, а также высокоуровневый дизайн, который функционально покрывает показ списка потенциальных матчей и обработку свайпов. Не обязательно знать глубокие детали конкретных технологий, но ожидается дизайн, поддерживающий и обычные фильтры, и фильтры по геолокации. Также ожидается решение, которое не показывает повторно просмотренные профили.&lt;h4&gt;Senior&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Глубина экспертизы&lt;/strong&gt;: от Senior кандидата ожидания смещаются к глубине - примерно 60% ширины и 40% глубины. Нужно уметь уходить в детали там, где у вас есть практический опыт.&lt;p&gt;&lt;strong&gt;Продвинутый дизайн системы&lt;/strong&gt;: вы должны быть знакомы с современными принципами проектирования систем: различными технологиями, вариантами их использования и тем, как они сочетаются друг с другом.&lt;p&gt;&lt;strong&gt;Аргументация решений&lt;/strong&gt;: вы должны уметь ясно объяснять плюсы/минусы архитектурных решений и их влияние на масштабирование, производительность и поддерживаемость, проговаривая компромиссы.&lt;p&gt;&lt;strong&gt;Проактивность и решение проблем&lt;/strong&gt;: вы должны продемонстрировать сильные навыки решения проблем и проактивный подход. Это подразумевает обнаружение потенциальных проблем в ваших проектах и предложение улучшений. Вам необходимо уметь выявлять и устранять узкие места, оптимизировать производительность и обеспечивать надежность системы.&lt;p&gt;&lt;strong&gt;Задача Tinder&lt;/strong&gt;: от Senior кандидата ожидается, что вы быстро пройдете высокоуровневый дизайн и потратите время на детальное обсуждение масштабируемой генерации списка потенциальных матчей и корректного создания матчей. Ожидается, что вы будете проактивно проговаривать компромиссы для построения списка потенциальных матчей, иметь представление о типе индексов которые помогут делать это эффективно, и помнить, когда кэш списка потенциальных матчей становится устаревшим.&lt;h4&gt;Staff+&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Акцент на глубину&lt;/strong&gt;: от Staff+ кандидата ожидается глубокий разбор нюансов - примерно 40% ширины и 60% глубины. Важна демонстрация того, что, даже если вы не решали именно эту задачу раньше, вы решали достаточно похожих задач в реальном мире, чтобы уверенно спроектировать решение, опираясь на опыт.&lt;p&gt;Интервьюер понимает, что вы знаете основы (REST, нормализация данных и т. п.), так что вы можете быстро пройти это на high-level дизайне и перейти к самому интересному.&lt;p&gt;&lt;strong&gt;Высокая проактивность&lt;/strong&gt;: на этом уровне ожидается, что вы будете самостоятельно выявлять и решать проблемы. Это предполагает не только реагирование на проблемы по мере их возникновения, но и их прогнозирование и реализацию упреждающих решений.&lt;p&gt;&lt;strong&gt;Практическое применение технологий&lt;/strong&gt;: важно уметь говорить о применяемых технологиях не только в теории, но и как это делается на практике - конфигурации, эксплуатационные нюансы, типичные проблемы.&lt;p&gt;&lt;strong&gt;Решение проблем&lt;/strong&gt;: ожидаются сильные навыки решения проблем с учетом факторов масштабирования, производительности, надежности и поддерживаемости.&lt;p&gt;&lt;strong&gt;Задача Tinder&lt;/strong&gt;: от Staff+ кандидата ожидается высокое качество решений по сложным задачам, которые обсуждались выше. Сильные кандидаты глубоко разбирают каждую тему, от них также ожидается четкое понимание компромиссов между различными решениями и способность ясно их сформулировать.&lt;hr&gt;&lt;p&gt;Разборы задач по System Design:&lt;p&gt;&lt;a href=https://habr.com/ru/articles/1018516/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Проектируем Ticketmaster, сервис бронирования билетов&lt;/a&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/articles/1022478/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Проектируем Uber, сервис для заказа такси&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>pavsenin</author>
      <guid>https://habr.com/ru/articles/1026316/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1026316</guid>
      <pubDate>Thu, 30 Apr 2026 13:17:16 +0000</pubDate>
    </item>
    <item>
      <title>Как совместить работу и путешествие на 1000+ км (заметки собаки)</title>
      <link>https://habr.com/ru/companies/yoomoney/articles/1030156/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030156</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Всем привет! На связи Снитч. Формально — собака, по факту — наблюдатель. Иногда кажется, что я единственный в этом доме, кто по-настоящему понимает, как всё устроено: где лежат вкусняшки, когда пора гулять и почему люди так любят усложнять простые вещи.&lt;p&gt;Хотя эта история из 2023 года, она всё ещё актуальна. Эти заметки я нацарапал тогда (лапами, не судите строго). Мы с моими людьми — Сашей, она отвечает за поддержку корпоративных клиентов и клиентский сервис в ЮKassa, и Витей, он говорил, что занимается продуктом в ЮKassa, — решили проверить одну гипотезу. Можно ли одновременно работать, ехать куда-то далеко и при этом не превращаться в выжатый лимон? Ну и заодно — каково это, путешествовать с собакой (спойлер: &lt;span class=habrahidden&gt;нормально, если собака — я&lt;/span&gt;).&lt;p&gt;Маршрут был такой: Нижний Новгород → Санкт-Петербург → Карелия → Нижний Новгород.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f1b/dc1/450/f1bdc14507b93da3e122cd5c626b6438.png width=974 height=548 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f1b/dc1/450/f1bdc14507b93da3e122cd5c626b6438.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f1b/dc1/450/f1bdc14507b93da3e122cd5c626b6438.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h2&gt;Дорога: маршрут и практические наблюдения&lt;/h2&gt;&lt;p&gt;Ехали по классике: М7 до Владимира, потом М12, ЦКАД, и дальше М11 до Питера.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d44/922/775/d44922775c3bd4ec41c1dbd04e1880b3.png alt=&#34;Сонное утреннее селфи! Кстати, это мои хозяева: Саша и Витя. Эти копуши хотели выехать в 7:00, но кто-то очень долго спит😊&#34; title=&#34;Сонное утреннее селфи! Кстати, это мои хозяева: Саша и Витя. Эти копуши хотели выехать в 7:00, но кто-то очень долго спит😊&#34; width=291 height=386 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d44/922/775/d44922775c3bd4ec41c1dbd04e1880b3.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d44/922/775/d44922775c3bd4ec41c1dbd04e1880b3.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Сонное утреннее селфи! Кстати, это мои хозяева: Саша и Витя. Эти копуши хотели выехать в 7:00, но кто-то очень долго спит😊&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Я, как главный специалист по высовыванию морды в окно, могу сказать:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Платные трассы — способ сохранить нервы. М11 — это стабильная скорость, хорошее покрытие и меньше стресса. Люди не ворчат, а значит, мне спокойнее.&lt;li&gt;&lt;p&gt;Заправки лучше планировать. Когда у людей заканчивался бензин, они почему-то нервничали сильнее, чем когда у меня заканчивался корм. Странно.&lt;li&gt;&lt;p&gt;Остановки каждые 2–3 часа — must have. И речь не только о собаках. Людям тоже полезно иногда выйти из машины и вспомнить, что у них есть ноги.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/294/97b/ace/29497bacee85e76d807bdc86c7040880.png alt=&#34;Фото 1. Я возмущён, что мне вместо кофе налили водички.  Фото 2. Всем давали солянку, а мне сухой корм. Облом!&#34; title=&#34;Фото 1. Я возмущён, что мне вместо кофе налили водички.  Фото 2. Всем давали солянку, а мне сухой корм. Облом!&#34; width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/294/97b/ace/29497bacee85e76d807bdc86c7040880.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/294/97b/ace/29497bacee85e76d807bdc86c7040880.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Фото 1. Я возмущён, что мне вместо кофе налили водички.&lt;br&gt;Фото 2. Всем давали солянку, а мне сухой корм. Облом!&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Важно помнить, что дорога — это не место для подвига, а часть путешествия. Чем спокойнее едешь, тем больше сил остаётся на саму поездку и на игры с четвероногими. Мы доехали часов за 12–13 без подвигов. И, что важно, никто не страдал.&lt;h2&gt;Петербург: работа днём, жизнь вечером&lt;/h2&gt;&lt;p&gt;В Санкт-Петербурге мы пожили несколько дней. Формат простой: днём люди работают, вечером — гуляют (и я, наконец, живу свою лучшую жизнь).&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/2ee/9ad/fe2/2ee9adfe22318c95492aefaada56e23c.png alt=&#34;А лежать на марсовом поле — бесценно.&#34; title=&#34;А лежать на марсовом поле — бесценно.&#34; width=336 height=450 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/2ee/9ad/fe2/2ee9adfe22318c95492aefaada56e23c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/2ee/9ad/fe2/2ee9adfe22318c95492aefaada56e23c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;А лежать на марсовом поле — бесценно.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Что я заметил:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Город дружелюбен к собакам. Меня пускали в кафе, предлагали воду. В одном месте даже посмотрели с уважением.&lt;li&gt;&lt;p&gt;Зелени много. Есть где побегать, даже если вокруг сплошная архитектура и люди, которые куда-то спешат.&lt;li&gt;&lt;p&gt;Передвигаться легко: пешком + метро — и ты уже в другом конце города нюхаешь новые запахи.&lt;/ul&gt;&lt;p&gt;А люди в это время… работали. Созвоны, задачи, какие-то спринты (я до сих пор думаю, что это про бег, но они почему-то сидят за ноутбуками).&lt;p&gt;Со стороны (с моего, самого объективного взгляда) это выглядело так: всё работает там, где есть доверие и общее понимание задач. Никто не занимается микроменеджментом или учётом времени — в центре внимания остаётся результат и его качество.&lt;h2&gt;Карелия: сидим и смотрим&lt;/h2&gt;&lt;p&gt;Потом мы поехали в Карелию, там ритм резко замедлился. Даже я стал меньше суетиться (но это не точно).&lt;p&gt;Если по точкам:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Глэмпинг у Вуоксы&lt;/strong&gt;&lt;br&gt;Хороший вариант, если хочется природы, но не хочется спать на шишках.&lt;br&gt;Важно: ингредиенты для приготовления еды лучше купить заранее. Люди без пищи становятся менее адекватными, проверено.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/837/67a/776/83767a77637da370e462719e687e3450.png width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/837/67a/776/83767a77637da370e462719e687e3450.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/837/67a/776/83767a77637da370e462719e687e3450.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Долина водопадов&lt;/strong&gt;&lt;br&gt;Красиво и спокойно: деревянные тропы, вода, лес. Можно идти и не думать о сложном. Я одобряю такие маршруты.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/76e/831/d89/76e831d896b619d89be18bba361a5255.png width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/76e/831/d89/76e831d896b619d89be18bba361a5255.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/76e/831/d89/76e831d896b619d89be18bba361a5255.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Рускеала&lt;/strong&gt;&lt;br&gt;Самое людное место, но не зря: большое, понятное, есть чем заняться. Люди брали билеты заранее, правильно делали, очереди я не люблю. В очередях мало кто гладит собак.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f01/28b/77b/f0128b77ba104c5257d883b36da7f526.png width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f01/28b/77b/f0128b77ba104c5257d883b36da7f526.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f01/28b/77b/f0128b77ba104c5257d883b36da7f526.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Онежское озеро&lt;/strong&gt;&lt;br&gt;Прогулки по берегу — идеальное место для тишины и созерцания. Летом можно искупаться, но будьте готовы, что собаки тоже захотят поплескаться. У нас было несколько экскурсий, и одна из них — прогулка по урочищу «Чёртов стул». Место окружено мистикой: здесь дважды проводились всероссийские съезды колдунов. Подниматься на сам «Чёртов стул» было непросто, но да, меня туда тоже взяли.&lt;/ul&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3b5/848/de9/3b5848de9e40751ecd2f4f3dbfe7de7f.png alt=&#34;Это я понял, что хозяева за мной не следят и убежал купаться.&#34; title=&#34;Это я понял, что хозяева за мной не следят и убежал купаться.&#34; width=433 height=542 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3b5/848/de9/3b5848de9e40751ecd2f4f3dbfe7de7f.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3b5/848/de9/3b5848de9e40751ecd2f4f3dbfe7de7f.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Это я понял, что хозяева за мной не следят и убежал купаться.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h2&gt;Наш рецепт работы в путешествии&lt;/h2&gt;&lt;p&gt;Самое интересное, как Саша и Витя совмещали работу и путешествие. Они поделились, что всё получилось благодаря трём ключевым факторам: чёткому планированию, выбору локаций со стабильным интернетом и возможности работать в гибридном формате.&lt;p&gt;При этом важно понимать, что формат совмещения работы и путешествий подходит не всем. Он не для вас, если:&lt;ul&gt;&lt;li&gt;&lt;p&gt;у вас нет чёткого плана и привычки ему следовать;&lt;li&gt;&lt;p&gt;вы хотите за короткое время успеть увидеть максимум — в таком режиме на работу обычно просто не остаётся времени;&lt;li&gt;&lt;p&gt;нет поддержки или команды на случай форс-мажоров;&lt;li&gt;&lt;p&gt;вам сложно концентрироваться, когда вокруг много нового и интересного;&lt;li&gt;&lt;p&gt;отсутствует базовая техническая готовность: стабильный интернет, рабочий ноутбук и связь;&lt;li&gt;&lt;p&gt;вы путешествуете один и делаете это впервые.&lt;/ul&gt;&lt;p&gt;В таких условиях работа и впечатления начинают скорее мешать друг другу, чем дополнять.&lt;p&gt;Саша и Витя заранее подбирали локации, где интернет работал стабильно, без зависаний и перебоев. Это позволяло спокойно выстроить рабочий день, а после его завершения — полностью переключаться на путешествие и новые впечатления.&lt;p&gt;Никакой магии, только грамотное планирование и дисциплина. Итоги поездки:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Проехали больше 1000 км.&lt;li&gt;&lt;p&gt;Сменили несколько локаций.&lt;li&gt;&lt;p&gt;Сохранили полную вовлечённость в рабочие процессы: ни одна задача не зависла и дедлайны были соблюдены.&lt;li&gt;&lt;p&gt;Поддержали дружескую атмосферу: мы не устали друг от друга, а это, пожалуй, главный индикатор того, что баланс работы и отдыха был выстроен идеально.&lt;/ul&gt;&lt;h2&gt;Немного личного (собачьего)&lt;/h2&gt;&lt;p&gt;Сейчас, оглядываясь на эти заметки, я понимаю: эта поездка — настоящая история. Жизнь всегда движется вперёд: роли меняются, маршруты тоже. Но такие путешествия почему-то остаются в памяти, прошу прощения за собачьи сравнения, как хороший запах, который долго не выветривается.&lt;p&gt;Ну и мой главный вывод: путешествовать с собакой — это нормально. Главное помнить, что собака тоже участник процесса, а не багаж.&lt;hr&gt;&lt;p&gt;А теперь вопрос, чтобы не просто разойтись: как вы выстраиваете баланс между работой и жизнью в поездках — это чёткое расписание или скорее «как пойдёт»? И пробовали ли вообще совмещать работу с дорогой (и собакой)?&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/yoomoney/articles/1030156/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030156</guid>
      <pubDate>Thu, 30 Apr 2026 13:13:23 +0000</pubDate>
    </item>
    <item>
      <title>Коллаборация человека и ИИ в Kotlin‑разработке: скрытые практики эффективных команд</title>
      <link>https://habr.com/ru/companies/otus/articles/1022118/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1022118</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Всем привет, меня зовут Сергей Прощаев. Я Tech Lead и руководитель направления Java / &lt;a href=https://otus.pw/0H6L/&gt;Kotlin&lt;/a&gt; разработки в FinTech, а также преподаю на курсах по архитектуре и бэкенд‑разработке в OTUS. В этой статье хочу разрушить один опасный миф: якобы скоро нейросети заменят разработчиков, и нам останется только пить кофе, пока ИИ пишет микросервисы.&lt;p&gt;Спойлер: это не так.&lt;p&gt;Сегодня мы перейдем к практике создания функционального узла системы. Мы не будем полагаться только на магию ИИ — мы совместим инженерный опыт, готовые архитектурные шаблоны и возможности автономных агентов. Это демонстрация реального процесса, где разработчик выступает в роли дирижера сложной технологической системы. Расскажу, как подготовить фундамент, как ставить задачи агентам и, что самое важное, где автоматика ошибается и требует твердой руки эксперта.&lt;h3&gt;Агенты не пишут код. Они решают задачи&lt;/h3&gt;&lt;p&gt;Когда я только начинал экспериментировать со связкой Kotlin + AI‑агенты, я думал, как и многие: «Сейчас объясню бизнес‑требование, и оно само превратится в Pull Request». На деле получил лапшу из классов, нарушающую все принципы &lt;abbr class=habraabbr title=&#34;пять принципов объектно-ориентированного проектирования (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion). Соблюдение этих принципов помогает строить гибкие и поддерживаемые системы, где изменения в одной части не ломают всё остальное.&#34; data-title=&#34;&amp;lt;p&amp;gt;пять принципов объектно-ориентированного проектирования (Single Responsibility, Open/Closed, Liskov Substitution, Interface Segregation, Dependency Inversion). Соблюдение этих принципов помогает строить гибкие и поддерживаемые системы, где изменения в одной части не ломают всё остальное.&amp;lt;/p&amp;gt;&#34; data-abbr=SOLID&gt;SOLID&lt;/abbr&gt;. Потому что ИИ, будь то Copilot, Cursor с агентами или самописные воркеры на базе &lt;abbr class=habraabbr title=&#34;Java/Kotlin-версия популярной библиотеки LangChain, предназначенная для упрощения разработки приложений с использованием больших языковых моделей (LLM). Помогает организовывать цепочки вызовов к ИИ-моделям, управлять памятью диалогов и агентами.&#34; data-title=&#34;&amp;lt;p&amp;gt;Java/Kotlin-версия популярной библиотеки LangChain, предназначенная для упрощения разработки приложений с использованием больших языковых моделей (LLM). Помогает организовывать цепочки вызовов к ИИ-моделям, управлять памятью диалогов и агентами.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&#34; data-abbr=LangChain4j&gt;LangChain4j&lt;/abbr&gt;, отлично решает задачу «в лоб». Но плохо понимает контекст всей системы, если этот контекст ему не дать.&lt;p&gt;Можно потратить месяц, переписывая один модуль &lt;abbr class=habraabbr title=&#34;легковесный фреймворк для создания веб-приложений и HTTP-сервисов на Kotlin, разработанный JetBrains. В отличие от классического Spring, полностью построен на корутинах и предоставляет гибкий DSL для маршрутизации и обработки запросов.&#34; data-title=&#34;&amp;lt;p&amp;gt;легковесный фреймворк для создания веб-приложений и HTTP-сервисов на Kotlin, разработанный JetBrains. В отличие от классического Spring, полностью построен на корутинах и предоставляет гибкий DSL для маршрутизации и обработки запросов.&amp;lt;/p&amp;gt;&#34; data-abbr=Ktor&gt;Ktor&lt;/abbr&gt;‑приложения, где ИИ‑агент генерировал эндпоинты без спецификации &lt;abbr class=habraabbr title=&#34;стандарт описания REST API в виде JSON/YAML-документа. По такой спецификации можно автоматически сгенерировать клиентский код, документацию и даже серверные заглушки, гарантируя согласованность между фронтендом и бэкендом.&#34; data-title=&#34;&amp;lt;p&amp;gt;стандарт описания REST API в виде JSON/YAML-документа. По такой спецификации можно автоматически сгенерировать клиентский код, документацию и даже серверные заглушки, гарантируя согласованность между фронтендом и бэкендом.&amp;lt;/p&amp;gt;&#34; data-abbr=OpenAPI&gt;OpenAPI&lt;/abbr&gt;. Каждый новый метод будет работать, но вместе все это превращается в кашу. Именно тогда я и сформулировал правило: «Кодинг — это доставка. Архитектурный дизайн и контракты — рулевое управление». И рулить должен разработчик (рис. 1).&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/454/e4c/414/454e4c41470cc75e662746d6cd0d44fe.png alt=&#34;Рис. 1 Иллюстрация концепции: разработчик как дирижер ИИ-команды&#34; title=&#34;Рис. 1 Иллюстрация концепции: разработчик как дирижер ИИ-команды&#34; width=1660 height=948 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/454/e4c/414/454e4c41470cc75e662746d6cd0d44fe.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/454/e4c/414/454e4c41470cc75e662746d6cd0d44fe.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис. 1 Иллюстрация концепции: разработчик как дирижер ИИ‑команды&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Благодаря этому у меня сформировалось понимание того, что подход &lt;abbr class=habraabbr title=&#34;парадигма, в которой программа строится как набор автономных агентов, взаимодействующих друг с другом. В контексте ИИ сегодня это чаще всего означает, что задачи декомпозируются и параллельно решаются несколькими интеллектуальными агентами под координацией человека или оркестратора.&#34; data-title=&#34;&amp;lt;p&amp;gt;парадигма, в которой программа строится как набор автономных агентов, взаимодействующих друг с другом. В контексте ИИ сегодня это чаще всего означает, что задачи декомпозируются и параллельно решаются несколькими интеллектуальными агентами под координацией человека или оркестратора.&amp;lt;/p&amp;gt;&#34; data-abbr=&#34;Agent-Oriented Programming&#34;&gt;Agent‑Oriented Programming&lt;/abbr&gt; имеет место быть, но с жестким контролем. Весь входящий поток бизнес‑требований необходимо сначала «упаковывать» в строгую архитектурную схему, используя проверенные шаблоны. Только после этого даем волю ИИ‑команде.&lt;h3&gt;Подготовка фундамента: не дайте агенту сойти с рельсов&lt;/h3&gt;&lt;p&gt;Главная ошибка новичков — дать агенту промпт: «Создай CRUD для пользователей на Ktor с &lt;abbr class=habraabbr title=&#34;ORM-библиотека от JetBrains для работы с SQL-базами данных на Kotlin. Позволяет описывать запросы как цепочки вызовов на Kotlin (типобезопасно), избегая сырых SQL-строк и ошибок на уровне компиляции.&#34; data-title=&#34;&amp;lt;p&amp;gt;ORM-библиотека от JetBrains для работы с SQL-базами данных на Kotlin. Позволяет описывать запросы как цепочки вызовов на Kotlin (типобезопасно), избегая сырых SQL-строк и ошибок на уровне компиляции.&amp;lt;/p&amp;gt;&#34; data-abbr=Exposed&gt;Exposed&lt;/abbr&gt;». Он создаст. Но как потом это поддерживать? Без абстракций, без &lt;abbr class=habraabbr title=&#34;шаблон, при котором бизнес-логика приложения находится в центре и не зависит от баз данных, фреймворков и UI. Все внешние зависимости «подключаются» через интерфейсы, что упрощает тестирование и замену компонентов.&#34; data-title=&#34;&amp;lt;p&amp;gt;шаблон, при котором бизнес-логика приложения находится в центре и не зависит от баз данных, фреймворков и UI. Все внешние зависимости «подключаются» через интерфейсы, что упрощает тестирование и замену компонентов.&amp;lt;/p&amp;gt;&#34; data-abbr=&#34;Clean Architecture&#34;&gt;Clean Architecture&lt;/abbr&gt;, без разделения на слои.&lt;p&gt;Мой Best Practice теперь выглядит так: &lt;strong&gt;шаблон решает всё&lt;/strong&gt;. Я не стартую проект с пустого &lt;code&gt;main.kt&lt;/code&gt;, а использую stage‑driven генерацию (пошаговое создание структуры).&lt;p&gt;Сначала необходимо вручную или с помощью второго агента‑архитектора создает каркас. Например, для типового микросервиса на Kotlin можно использовать следующий скелет:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;domain&lt;/code&gt; — модели и чистые интерфейсы репозиториев (никаких фреймворков).&lt;li&gt;&lt;p&gt;&lt;code&gt;usecases&lt;/code&gt; — бизнес‑логика, которая дергает интерфейсы.&lt;li&gt;&lt;p&gt;&lt;code&gt;adapters&lt;/code&gt; — реализации репозиториев (Exposed, Ktor Client), контроллеры.&lt;/ul&gt;&lt;p&gt;Я называю это «подготовкой загона». Агентам по разработке я разрешаю писать код только внутри контрактов, определенных интерфейсами. Если бизнес говорит: «Нужна авторизация через госключ», мы не просим ИИ сразу писать код. Мы просим:&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Агента‑аналитика:&lt;/strong&gt; Проанализируй вводные и предложи модель данных и sequence‑диаграмму (Mermaid).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Человека:&lt;/strong&gt; Утверди диаграммы, добавь нефункциональные требования (NFR).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Агента‑разработчика:&lt;/strong&gt; Имплементируй строго по утвержденной спецификации в подготовленных файлах.&lt;/ol&gt;&lt;h3&gt;Работа с ИИ‑командой: декомпозиция под надзором&lt;/h3&gt;&lt;p&gt;Перейдем к практике. Допустим, задача — сделать функциональный узел «Управление подписками пользователей» на Kotlin.&lt;p&gt;Стараюсь использовать автономных агентов для первичной декомпозиции, но с инженерным контролем.&lt;p&gt;Я даю агенту промпт: «Распиши Use Case „Оформление подписки“. Учти, что платеж проходит через внешний эквайринг, возможна задержка колбэка до 30 секунд, пользователь должен видеть актуальный статус в реальном времени».&lt;p&gt;Агент выдает:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Сохранить запись в БД со статусом &lt;code&gt;PENDING&lt;/code&gt;.&lt;li&gt;&lt;p&gt;Сходить в эквайринг.&lt;li&gt;&lt;p&gt;Обновить статус.&lt;/ol&gt;&lt;p&gt;Как человек, смотрю на это и вижу фатальную дыру. А если эквайринг отвечает долго, а пользователь нажал кнопку 5 раз подряд? &lt;abbr class=habraabbr title=&#34;свойство операции, при котором многократное повторение одного и того же запроса приводит к тому же результату, что и однократное выполнение. Например, повторное нажатие кнопки «Оформить подписку» не должно создать несколько подписок или снять деньги дважды.&#34; data-title=&#34;&amp;lt;p&amp;gt;свойство операции, при котором многократное повторение одного и того же запроса приводит к тому же результату, что и однократное выполнение. Например, повторное нажатие кнопки «Оформить подписку» не должно создать несколько подписок или снять деньги дважды.&amp;lt;/p&amp;gt;&#34; data-abbr=Идемпотентность&gt;Идемпотентность&lt;/abbr&gt;! Агент часто игнорирует идемпотентность, если ему не напомнить.&lt;p&gt;Дальше подключаем второго агента для написания интеграционных тестов. Мы не пишем тесты руками. Мы скармливаем ему спецификацию в формате &lt;abbr class=habraabbr title=&#34;человекочитаемый язык описания поведения системы без привязки к коду. Формат Given (дано) / When (когда) / Then (тогда) позволяет описывать тестовые сценарии, которые понимают и бизнес-заказчики, и автоматизированные тестовые фреймворки.&#34; data-title=&#39;&amp;lt;p&amp;gt;человекочитаемый язык описания поведения системы без привязки к коду. Формат&amp;amp;nbsp;&amp;lt;code data-mark=&#34;code&#34;&amp;gt;Given (дано) / When (когда) / Then (тогда)&amp;lt;/code&amp;gt;&amp;amp;nbsp;позволяет описывать тестовые сценарии, которые понимают и бизнес-заказчики, и автоматизированные тестовые фреймворки.&amp;lt;/p&amp;gt;&#39; data-abbr=Gherkin&gt;Gherkin&lt;/abbr&gt; (Given/When/Then), и он генерирует Kotlin‑код с использованием &lt;code&gt;kotest&lt;/code&gt; и test containers. Человек на этом этапе следит только за одним: покрыты ли тестами те самые граничные условия, которые нашел архитектор.&lt;p&gt;Посмотрите на диаграмму последовательности ниже. Это результат работы связки «Человек‑Архитектор + ИИ‑аналитик». Мы специально развели синхронные и асинхронные вызовы, чтобы не связывать основной поток пользователя с внутренней кухней платежей (рис. 2).&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ba9/4fa/ab2/ba94faab229cce066079d6ec7ef4d74e.png alt=&#34;Рис. 2 Диаграмма последовательности: идемпотентное оформление подписки с разделением на синхронный ответ и асинхронный callback&#34; title=&#34;Рис. 2 Диаграмма последовательности: идемпотентное оформление подписки с разделением на синхронный ответ и асинхронный callback&#34; width=1241 height=609 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ba9/4fa/ab2/ba94faab229cce066079d6ec7ef4d74e.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ba9/4fa/ab2/ba94faab229cce066079d6ec7ef4d74e.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Рис. 2 Диаграмма последовательности: идемпотентное оформление подписки с разделением на синхронный ответ и асинхронный callback&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Схема показывает, как мы развели синхронную и асинхронную ветки в сценарии оформления подписки. Пользователь получает моментальный ответ 202 Accepted и не ждёт, пока отработает платёж. В этот момент запись уже лежит в базе в статусе PENDING, а событие ушло в очередь. Дальше фоновый агент спокойно общается с эквайрингом (до 30 секунд) и обновляет статус, не нагружая основной пользовательский поток. Ключевой момент — insertOrIgnore с идемпотентным ключом, который предотвращает дублирование подписок при повторных запросах.&lt;p&gt;Таким образом, схема еще раз иллюстрирует центральную идею: &lt;strong&gt;архитектор‑дирижёр определяет, что делается синхронно, а что — асинхронно, какие контракты использовать, а ИИ‑команда исполняет в этих рамках&lt;/strong&gt;. Без такого разделения мы бы получили хрупкий сервис, склонный к каскадным отказам!&lt;h3&gt;Инженерный контроль: когда ИИ ошибается красиво и незаметно&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Правило, которое я вывел:&lt;/strong&gt; автоматика отлично справляется с happy path. Как только начинаются сетевые проблемы, гонки состояний или обратное давление (&lt;abbr class=habraabbr title=&#34;механизм в асинхронных или потоковых системах, который не позволяет быстрому производителю перегружать медленного потребителя. Например, если сервис не успевает обрабатывать сообщения, он сигнализирует отправителю снизить темп, а не бездумно накапливает очередь до падения по памяти.&#34; data-title=&#34;&amp;lt;p&amp;gt;механизм в асинхронных или потоковых системах, который не позволяет быстрому производителю перегружать медленного потребителя. Например, если сервис не успевает обрабатывать сообщения, он сигнализирует отправителю снизить темп, а не бездумно накапливает очередь до падения по памяти.&amp;lt;/p&amp;gt;&#34; data-abbr=backpressure&gt;backpressure&lt;/abbr&gt;) — нужен инженерный контроль. ИИ сегодня — это мощный, но все еще Junior‑разработчик. А когда Junior пишет сложный многопоточный код в продакшен, за ним нужен Lead.&lt;p&gt;Чтобы минимизировать риски, можно использовать практику «Авто‑ревью». Один ИИ‑агент генерирует код, второй (с промптом «ты вредный сеньор‑проверяльщик») ищет типовые уязвимости: блокировки на уровне БД, утечки &lt;abbr class=habraabbr title=&#34;в Kotlin это механизм для асинхронного программирования без блокировок потоков. Позволяет писать последовательный, линейный код, который под капотом выполняется неблокирующим образом, что критично для высоконагруженных сервисов.&#34; data-title=&#34;&amp;lt;p&amp;gt;в Kotlin это механизм для асинхронного программирования без блокировок потоков. Позволяет писать последовательный, линейный код, который под капотом выполняется неблокирующим образом, что критично для высоконагруженных сервисов.&amp;lt;/p&amp;gt;&#34; data-abbr=корутин&gt;корутин&lt;/abbr&gt;, нарушения контрактов. И роль человека — принимать окончательное решение в спорных ситуациях между ними.&lt;h3&gt;История одного рефакторинга: от хаоса к оркестру&lt;/h3&gt;&lt;p&gt;Как‑то в сети мне попалась история, когда команда переводила легаси‑проект с Java на Kotlin и прикрутить умный поиск. Бизнес хотел «как в Google, но внутри ERP». Изначально команда пыталась просто писать промпты в ChatGPT и вставлять код. Закончилось это тем, что сервис поиска падал при любом запросе длиннее трех слов.&lt;p&gt;Но после применения коллаборативного подхода все взлетело. Был взят шаблон &lt;abbr class=habraabbr title=&#34;Порты и адаптеры (шестигранная архитектура) — архитектурный стиль, где ядро приложения общается с внешним миром через «порты» (интерфейсы), а конкретные реализации (адаптеры) подключаются снаружи. Это позволяет легко заменять базу данных, систему очередей или протокол взаимодействия, не трогая бизнес-логику.&#34; data-title=&#34;&amp;lt;p&amp;gt;&amp;lt;strong&amp;gt;Порты и адаптеры&amp;lt;/strong&amp;gt;&amp;amp;nbsp;(шестигранная архитектура) — архитектурный стиль, где ядро приложения общается с внешним миром через «порты» (интерфейсы), а конкретные реализации (адаптеры) подключаются снаружи. Это позволяет легко заменять базу данных, систему очередей или протокол взаимодействия, не трогая бизнес-логику.&amp;lt;/p&amp;gt;&#34; data-abbr=&#34;Ports and Adapters&#34;&gt;Ports and Adapters&lt;/abbr&gt; (Hexagonal Architecture):&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Разработчик&lt;/strong&gt; спроектировал порт &lt;code&gt;SearchEnginePort&lt;/code&gt; с одним методом.&lt;li&gt;&lt;p&gt;&lt;strong&gt;ИИ‑агент&lt;/strong&gt; сгенерировал три адаптера: простой полнотекстовый поиск, поиск через векторную базу (pgvector) и гибридный с ранжированием.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Архитектор&lt;/strong&gt; (человек) написал фабрику, переключающую стратегии в зависимости от типа запроса без перезагрузки сервиса.&lt;/ol&gt;&lt;p&gt;В итоге это решение стало обрабатывать 500 RPS на довольно скромных контейнерах. Но главное не цифры, а то, как быстро были перебраны гипотезы. Без агентов написание трех параллельных адаптеров заняло бы недели две. С ними команде удалось сделать прототип за 3 дня. Но дирижировал процессом человек. Именно он решил, что &lt;abbr class=habraabbr title=&#34;расширение PostgreSQL, добавляющее векторный тип данных и операции для поиска по сходству (similarity search). Используется для построения рекомендательных систем, умного поиска и AI-приложений без необходимости поднимать отдельные векторные базы.&#34; data-title=&#34;&amp;lt;p&amp;gt;расширение PostgreSQL, добавляющее векторный тип данных и операции для поиска по сходству (similarity search). Используется для построения рекомендательных систем, умного поиска и AI-приложений без необходимости поднимать отдельные векторные базы.&amp;lt;/p&amp;gt;&#34; data-abbr=pgvector&gt;pgvector&lt;/abbr&gt; надо использовать не напрямую, а через обертку, чтобы избежать проблем с драйвером PostgreSQL!&lt;h3&gt;Результат: пошаговое создание работающего функционала&lt;/h3&gt;&lt;p&gt;Как это выглядит в итоге на проекте?&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Инициализация:&lt;/strong&gt; берем готовый Gradle‑шаблон с Ktor, Exposed, &lt;abbr class=habraabbr title=&#34;простой и легковесный DI-фреймворк для Kotlin. Не использует аннотации и генерацию кода, основан на DSL, что делает его популярным выбором для Ktor-приложений и мобильной разработки на Android.&#34; data-title=&#34;&amp;lt;p&amp;gt;простой и легковесный DI-фреймворк для Kotlin. Не использует аннотации и генерацию кода, основан на DSL, что делает его популярным выбором для Ktor-приложений и мобильной разработки на Android.&amp;lt;/p&amp;gt;&#34; data-abbr=Koin&gt;Koin&lt;/abbr&gt;. (1 минута).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Анализ:&lt;/strong&gt; загружаем требования в NotebookLM или LangChain‑агента, получаем каркас Use Case и диаграмму (Рис. 2).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Контракты:&lt;/strong&gt; разработчик‑человек пишет 20–30 строк интерфейсов в &lt;code&gt;domain&lt;/code&gt; и &lt;code&gt;usecases&lt;/code&gt;. Это и есть границы дозволенного для ИИ.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Реализация:&lt;/strong&gt; агент генерирует &lt;code&gt;DefaultSubscriptionUseCase&lt;/code&gt; и &lt;code&gt;SubscriptionController&lt;/code&gt;. Код компилируется с первого раза, потому что контракты жестко заданы типами Kotlin.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Проверка:&lt;/strong&gt; второй агент генерирует &lt;abbr class=habraabbr title=&#34;подход к тестированию, при котором вместо конкретных примеров задаются свойства, которые должны выполняться для широкого диапазона случайных входных данных. Фреймворк сам генерирует сотни тестовых случаев и ищет нарушения.&#34; data-title=&#34;&amp;lt;p&amp;gt;подход к тестированию, при котором вместо конкретных примеров задаются свойства, которые должны выполняться для широкого диапазона случайных входных данных. Фреймворк сам генерирует сотни тестовых случаев и ищет нарушения.&amp;lt;/p&amp;gt;&#34; data-abbr=&#34;property-based тесты&#34;&gt;property‑based тесты&lt;/abbr&gt;, проверяет на утечки по шаблонам. Человек проверяет критический сценарий с таймаутами.&lt;/ol&gt;&lt;h3&gt;Заключение: дирижер, а не исполнитель&lt;/h3&gt;&lt;p&gt;Возвращаясь к началу статьи: стоит ли бояться ИИ? Нет. Стоит ли учиться с ним работать по‑новому? Однозначно да!&lt;p&gt;Мир уходит от ремесленного написания тысяч строк однотипного кода. Ценность разработчика смещается в область архитектурных решений, понимания компромиссов и настройки автономных агентов. Это как переход от ручного труда к работе оператора сложного станка с ЧПУ. Вроде бы работу делает станок, но без мастера, который его настроит и проконтролирует, он настрогает брака.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/7dc/71c/d15/7dc71cd15aa304ba517d579c4b19f88b.png width=2640 height=300 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/7dc/71c/d15/7dc71cd15aa304ba517d579c4b19f88b.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/7dc/71c/d15/7dc71cd15aa304ba517d579c4b19f88b.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Если вам близок подход «ИИ не вместо разработчика, а как часть инженерной системы», тему можно разобрать глубже на &lt;a href=https://otus.pw/VJ9qw/&gt;курсе &lt;strong&gt;«Проектирование и разработка Kotlin‑бэкенда»&lt;/strong&gt;&lt;/a&gt;. Он подойдет тем, кто хочет не просто писать код на Kotlin, а проектировать backend‑сервисы с понятными контрактами, архитектурными границами, тестируемой бизнес‑логикой и устойчивостью к реальным продакшен‑сценариям.&lt;p&gt;&lt;em&gt;Познакомиться с подходом можно на бесплатных открытых уроках курса. Их проводят преподаватели‑практики: можно увидеть реальные сценарии работы с Kotlin, архитектурными шаблонами и ИИ‑агентами, протестировать формат обучения и задать вопросы экспертам.&lt;/em&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;6 мая в 19:00&lt;/strong&gt; — &lt;a href=https://otus.pw/6OuA/&gt;«Разработка проекта на Kotlin: коллаборация человека, архитектурных шаблонов и ИИ‑команды»&lt;/a&gt;.&lt;sub&gt;&lt;/sub&gt;&lt;br&gt;&lt;sub&gt;Разберём, как перейти от идеи к функциональному узлу системы: где помогает ИИ, где нужны архитектурные ограничения и почему разработчик всё равно остается тем, кто управляет процессом.&lt;/sub&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;20 мая в 19:00&lt;/strong&gt; — &lt;a href=https://otus.pw/W7kM/&gt;«Эволюция Kotlin‑разработчика: программирование бизнес‑процессов и управление ИИ‑агентами»&lt;/a&gt;.&lt;br&gt;&lt;sub&gt;Поговорим о том, как меняется роль backend‑разработчика: от ручного написания кода к проектированию процессов, постановке задач агентам и контролю инженерных решений.&lt;/sub&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/otus/articles/1022118/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1022118</guid>
      <pubDate>Thu, 30 Apr 2026 13:06:43 +0000</pubDate>
    </item>
    <item>
      <title>Мама, я не вайб-кодер</title>
      <link>https://habr.com/ru/articles/1030146/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030146</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Меня зовут Олег Балбеков, я основатель Evrone. Мы с командой делаем сложные цифровые продукты для клиентов по всему миру, и я в этой профессии уже почти 20 лет.&lt;p&gt;Но эта статья не про Evrone. Она про то, что разработка цифровых продуктов меняется быстрее, чем когда-либо за последние двадцать лет. И про то, как это выглядит, если ты внутри.&lt;p&gt;Мой мир, в котором я любил программировать руками, рухнул. Не потому, что я разучился, а потому, что я больше не буду этого делать никогда. Незачем. Я вижу, как это происходит со всеми, с кем я общаюсь – программистами, тимлидами, архитекторами. Кто-то сопротивляется, кто-то уже перешёл, но все понимают: профессия меняется, и быстро.&lt;p&gt;А мой новый мир, в котором я снова создаю цифровые продукты – родился. На прошлой неделе я за несколько вечеров собрал работающий веб-сервис. Сам.&lt;p&gt;Не написав ни одной строчки кода.&lt;h3&gt;Откуда я смотрю&lt;/h3&gt;&lt;p&gt;Мне 42 года, и я думаю, что людям моего поколения с компьютерами по-настоящему повезло. Я родился в мире, в котором персональных компьютеров ещё не было, а к старшим классам школы ПК уже стоял у меня на столе.&lt;p&gt;В детстве я был настолько впечатлён компьютерами, что с головой ушёл в этот новый дивный мир. Ведь компьютеры в моём детстве – это был андеграунд, причастность к новой неформальной субкультуре, в которой тусили самые умные и странные пацаны города. Да, по шапке от уличной шпаны можно было получить легко, но тебя спасало чувство сопричастности к чему-то великому.&lt;p&gt;Раньше я много программировал, и до основания компании написал кучу кода. Однако, основав Evrone 18 лет назад, я примерно за первые десять лет полностью потерял возможность писать код. Так эволюционно случилось, что я стал менеджером, у которого сейчас от 6 до 8 совещаний в день.&lt;p&gt;Писать код теперь некогда, да и странно. А когда всё-таки очень хочется тряхнуть стариной – реальность бьёт по лицу. Чтобы написать хоть одну строчку, ты должен поднять окружение, обновить все либы, всё вспомнить. Тыкаешься с этим весь день, ловишь баги, и к концу дня, полностью задолбанный, понимаешь, что писать код… видимо, в другой раз.&lt;p&gt;Ты менеджер, дружище! Завтра снова восемь часов подряд будешь разговаривать на митапах. А в душе остаётся лишь гаденькое чувство, что ты чертовски скучаешь по старой движухе.&lt;p&gt;И тут в мир ворвались ИИ и агенты. Я начал пользоваться Claude Code – и мой мир перевернулся.&lt;h3&gt;Идея&lt;/h3&gt;&lt;p&gt;Кто читал мою &lt;a href=https://habr.com/ru/article/edit/1026336/ rel=&#34;noopener noreferrer nofollow&#34;&gt;предыдущую статью на Хабре&lt;/a&gt;, помнит Мимира – моего ИИ-братишку, который помогает мне вести Telegram-канал. Так вот, я подумал: если Мимир работает для меня – он может работать и для других.&lt;p&gt;Идея проекта простая. AI-помощник для редакторов Telegram-каналов. Подключаешь свои источники – другие каналы и RSS-ленты – а сервис сам читает, скорит посты по «горячести» и присылает в личку готовый черновик: рерайт текста плюс сгенерированная картинка. Тебе остаётся только нажать «Опубликовать» – и пост появляется в канале.&lt;p&gt;Технологически – смешение всего самого вкусного: Claude Sonnet для рерайта, fal.ai Flux для иллюстраций, MTProto и TDLib для чтения каналов. Чистый хайповый AI-стартап нового поколения.&lt;h3&gt;Неделя&lt;/h3&gt;&lt;p&gt;&lt;em&gt;Воскресенье.&lt;/em&gt; Дети сели делать уроки, я открыл Клода и набросал спецификацию. К вечеру из неё родилось техническое задание.&lt;p&gt;&lt;em&gt;Понедельник.&lt;/em&gt; ТЗ – это фундамент, от которого потом отталкивается агент, поэтому весь день мы с Клодом писали его вместе. Правили нестыковки, добавляли детали, проверяли, что ничего не противоречит само себе. К вечеру я перечитал документ и понял: да, это тот сервис, который я хочу создать.&lt;p&gt;&lt;em&gt;Вторник, первая половина.&lt;/em&gt; Попросил Клода переделать ТЗ в промпт для Claude Code. Это тоже ответственное дело: нужно убедиться, что все мысли и постановки переехали правильно. Что меня поняли. Что ничего лишнего, и ничего не забыто.&lt;p&gt;&lt;em&gt;Вторник, вторая половина.&lt;/em&gt; Мы с Клодом сели программировать. Я контролировал, что создаёт Claude Code – куда идёт, как структурирует код, какие принимает архитектурные решения. Где-то корректировал, где-то учил делать правильно, где-то возвращал и говорил «давай переписывать заново».&lt;p&gt;&lt;em&gt;Среда, утро.&lt;/em&gt; Появился первый драфт сервиса.&lt;p&gt;&lt;em&gt;Среда, вечер.&lt;/em&gt; Первая альфа.&lt;p&gt;&lt;em&gt;Четверг, утро.&lt;/em&gt; Бета-версия. Покрываем тестами.&lt;p&gt;&lt;em&gt;Четверг, вечер.&lt;/em&gt; Крепкий релиз-кандидат. Сделал лендинг.&lt;p&gt;&lt;em&gt;Пятница, утро.&lt;/em&gt; Финальные UI-правки.&lt;p&gt;&lt;em&gt;Пятница, обед.&lt;/em&gt; Зарелизил. Сервис в продакшене.&lt;h3&gt;Архитектура&lt;/h3&gt;&lt;p&gt;В двух словах: два сервера в разных странах, между ними сетевой туннель, на каждом сервере – свой технологический стек.&lt;p&gt;Один сервер – в России, на нём крутится основной продакшн: Rails-приложение, PostgreSQL, Redis, Sidekiq для фоновых задач, MinIO для хранения картинок, nginx с SSL – на нём живут пользователи, к нему подключается домен mimirjotun.ru.&lt;p&gt;Второй сервер – в Амстердаме, на нём отдельный микросервис на Python с FastAPI, который умеет ходить в Telegram по протоколу MTProto и читать каналы.&lt;p&gt;Между серверами – overlay-сеть на Tailscale, через которую они общаются друг с другом так, как будто стоят в одной стойке.&lt;p&gt;Сервис интегрируется с внешним миром: Anthropic Claude (рерайт постов), fal.ai Flux (генерация картинок), Telegram Bot API (доставка драфтов в личку), Telegram Login (вход в веб через виджет), Telegram MTProto и tdlib (чтение каналов). Внутри – десятки сервисных классов, фоновые джобы, очереди, скоринг постов с учётом просмотров, форвардов, реакций и реплаев. Админка с Basic Auth. Sidekiq Web для мониторинга. Sentry для ошибок. Lograge для логов. Bullet для отлова N+1. 279 зелёных тестов на RSpec.&lt;p&gt;Два сервиса деплоятся независимо – ни один не блокирует другой.&lt;h3&gt;А теперь следите за руками&lt;/h3&gt;&lt;p&gt;Всё, что я только что описал – Rails-приложение, две базы, фоновый процессинг, отдельный микросервис на Python в другой стране, туннель между ними, интеграции с тремя внешними API, тесты, мониторинг, деплой – раньше для меня означало бы недели работы команды разработчиков. Постановку задачи, разделение по людям, код-ревью, отладку, интеграционное тестирование. Это нормальный такой объём работ для хорошей и опытной команды с архитектором во главе.&lt;p&gt;Я собрал это один. За неделю. По два-три часа в день – и то с трудом наскребая эти часы между созвонами, рабочими решениями, сериалами с женой и доткой после отбоя. Никто не освобождал меня от обязанностей CEO. У меня по-прежнему по 6-8 митингов в день. Я не уходил в отпуск, не запирался в кабинете на неделю, не выбивал у себя «творческое время». Я просто между делами открывал Claude Code и продолжал с того места, где остановился.&lt;p&gt;И на выходе – работающий сервис. Не игрушка, не лендинг, не «MVP на коленке». Полноценный продукт со своей админкой, тестами и нормальным деплоем.&lt;h3&gt;Мама, я не вайб-кодер!&lt;/h3&gt;&lt;p&gt;Вот тут важный момент, на котором я хочу остановиться отдельно.&lt;p&gt;В интернете сейчас много разговоров про «вайб-кодинг» – когда чувак, не умеющий программировать, описывает агенту что хочет, и агент ему что-то выкатывает. Чаще всего это простой лендинг, поделка, прикольная игрушка. И автор такой: «Смотрите, я разработчик! Я сделал продукт!»&lt;p&gt;Так вот, я не вайб-кодил.&lt;p&gt;Я создавал продукт со знанием дела, с полным пониманием того, что происходит. С теми же костылями, с теми же архитектурными проблемами, которые нужно решить или избежать. С теми же ограничениями и возможностями железа. С теми же нюансами интеграций со сторонними сервисами. С теми же требованиями к окружению, сетям, протоколам.&lt;p&gt;Только я не писал код руками. Я занимался тем, что в индустрии начинают называть агентной разработкой – когда ты управляешь агентом, как тимлид тремя сильными мидлами: задаёшь задачу, проверяешь, корректируешь, иногда переписываешь. Только эти мидлы не устают, не отвлекаются и пишут в десять раз быстрее любого моего разработчика. Но я остаюсь архитектором продукта от первой строчки ТЗ до последней строчки в коде. Я понимаю каждое решение. Я знаю, почему здесь Sidekiq, а не Solid Queue. Почему PostgreSQL, а не MongoDB. Почему туннель через Tailscale, а не прямой VPN. Почему Hotwire, а не React.&lt;p&gt;Агент – это инструмент. Очень умный, очень быстрый, очень мощный. Но инструмент. И именно я решаю, что он делает.&lt;h3&gt;Возвращение в игру&lt;/h3&gt;&lt;p&gt;За двадцать лет в индустрии у меня было несколько сильных впечатлений – первая собственная программа, заработавшая в проде, первый клиент из Кремниевой долины, первая команда, которая выросла без меня. Управление агентом стоит где-то рядом.&lt;p&gt;Это другая профессия, которой раньше не существовало.&lt;p&gt;И вот что я понял за эту неделю. У людей нашего поколения снова появилась возможность создавать и творить – не уходя из своих компаний, не выбивая себе «творческое время», между митингами и сериалами с женой. Тот багаж знаний, который мы накопили за двадцать лет и думали, что больше всерьёз никогда не пригодится – снова работает. Просто теперь мы управляем не людьми, а машинами.&lt;p&gt;Смена технологического уклада обычно играет против старшего поколения. Ты не успеваешь за молодыми, у которых в руках свежие инструменты и новые фреймворки. Так было всегда. А сейчас, кажется, наоборот. Эпоха разворачивается в пользу опытных мужиков, которые прошли весь путь разработки. Все думали, что пора готовиться к пенсии и что наш опыт уже не очень-то и нужен. А оказалось – нам просто не хватало одного мощного инструмента, который сконвертирует этот опыт в профит!&lt;p&gt;И тот формат, который мир сейчас предлагает – это, возможно, один из самых потрясающих форматов, какой только можно было представить для ребят моего опыта и поколения. Это тот способ созидания, который подходит нам идеально.&lt;h3&gt;Похоже, что мне и моему поколению «компьютерщиков» снова повезло?&lt;/h3&gt;&lt;p&gt;Мы застали рождение персональных компьютеров – и помним, как это меняло мир. И, кажется, сейчас я чувствую то же самое.&lt;p&gt;Похоже, нам снова повезло застать смену технологической эпохи, сравнимую с появлением электричества или паровых машин. На наших глазах ручной труд программиста становится трудом «машинным». И я воспринимаю это как полноценную смену технологического уклада.&lt;p&gt;И сейчас я впечатлён всем этим не меньше, чем тогда, в детстве, когда впервые в жизни увидел компьютер в рекламе на экране пузатого телевизора.&lt;p&gt;Сопричастность 2.0.&lt;p&gt;Обсудим?&lt;hr&gt;&lt;p&gt;&lt;em&gt;Олег Балбеков, &lt;/em&gt;&lt;a href=https://t.me/obalbekov rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;em&gt;@obalbekov&lt;/em&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>Lxx</author>
      <guid>https://habr.com/ru/articles/1030146/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030146</guid>
      <pubDate>Thu, 30 Apr 2026 13:01:03 +0000</pubDate>
    </item>
    <item>
      <title>Тестируем программы для вскрытия биткойн-головоломок</title>
      <link>https://habr.com/ru/companies/ruvds/articles/1029952/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029952</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/25/c2/b6/25c2b6ef7b6be36742dcb02da4e492ca.jpg alt=&#34;Тест программ-решателей биткойн-головоломок&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/25/c2/b6/25c2b6ef7b6be36742dcb02da4e492ca.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/webt/25/c2/b6/25c2b6ef7b6be36742dcb02da4e492ca.jpg 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Тест программ-решателей биткойн-головоломок&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;После того как я написал статью &lt;a href=https://habr.com/ru/companies/ruvds/articles/934002/&gt;«Головоломка на 1000 BTC»&lt;/a&gt; мне стали писать в личку авторы программ для их решения. В этой статье я расскажу о целом семействе таких программ и протестирую все те, что попали мне в руки, на скорость.&lt;p&gt;А заодно расскажу, как авторы этих программ выжимают соки из железа.&lt;p&gt;&lt;em&gt;Речь идёт про головоломки, у которых известен только BTC-адрес формата P2PKH, но не известен публичный ключ. Это головоломки с номерами 71-159, кроме тех, чей номер кратен 5. У головоломок кратных 5 известен публичный ключ и для них применяются совсем другие алгоритмы — кенгуру Полларда или BSGS, на порядки более быстрые, чем bruteforce. Самая лёгкая ещё не вскрытая головоломка с известным публичным ключом — 135&lt;/em&gt;.&lt;h2&gt;Краткая хронология программ&lt;/h2&gt;&lt;p&gt;Я не претендую на полное знание этой сферы, поэтому, если я что-то упустил, , пишите в личку или в комментарии&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Год начала разработки&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Автор&lt;th&gt;&lt;p align=left&gt;Платформа&lt;th&gt;&lt;p align=left&gt;Язык&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2018&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;BitCrack&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;brichard19&lt;td&gt;&lt;p align=left&gt;CUDA + OpenCL&lt;td&gt;&lt;p align=left&gt;C++&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2019&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;VanitySearch&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;JeanLucPons&lt;td&gt;&lt;p align=left&gt;CPU + CUDA&lt;td&gt;&lt;p align=left&gt;C++&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;KeyHunt&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;albertobsd&lt;td&gt;&lt;p align=left&gt;CPU&lt;td&gt;&lt;p align=left&gt;C++&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;KeyHunt-Cuda&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Qalander&lt;td&gt;&lt;p align=left&gt;CUDA&lt;td&gt;&lt;p align=left&gt;C++&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Cyclone&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;dookoo2&lt;td&gt;&lt;p align=left&gt;CPU (AVX2/512)&lt;td&gt;&lt;p align=left&gt;C++&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;ecloop&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;vladkens&lt;td&gt;&lt;p align=left&gt;CPU&lt;td&gt;&lt;p align=left&gt;чистый C&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;CUDACyclone&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;dookoo2&lt;td&gt;&lt;p align=left&gt;CUDA&lt;td&gt;&lt;p align=left&gt;CUDA&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;BtcMole&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;keymole&lt;td&gt;&lt;p align=left&gt;CPU (x86-64, ARM64) + CUDA + AMDGPU&lt;td&gt;&lt;p align=left&gt;closed source&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3&gt;BitCrack (2018)&lt;/h3&gt;&lt;p&gt;Старейшина жанра. Первая массовая программа для перебора приватных ключей на GPU, поддерживает и CUDA, и OpenCL. Сегодня морально устарела — отстаёт от лидеров примерно в 5 раз. Технические причины разберём в разделе «Что используют авторы для ускорения перебора».&lt;h3&gt;VanitySearch (2019)&lt;/h3&gt;&lt;p&gt;Программа Жан-Люка Понса (JeanLucPons) задумывалась для генерации «красивых» биткойн-адресов (&lt;code&gt;1Love…&lt;/code&gt;, &lt;code&gt;1Btc…&lt;/code&gt;), но архитектурно оказалась настолько удачна, что почти все последующие проекты связанные с биткойн-головоломками — её прямые потомки или форки. Канонические трюки массовой инверсии Монтгомери и работы с PTX зародились именно здесь.&lt;h3&gt;KeyHunt (2021)&lt;/h3&gt;&lt;p&gt;Программа-комбайн для CPU от &lt;a href=https://github.com/albertobsd&gt;Alberto&lt;/a&gt;: поддерживает Bloom-фильтр по списку адресов, режим &lt;strong&gt;BSGS&lt;/strong&gt; (Baby-step Giant-step) для подбора по известному pubkey, несколько типов целей (BTC, ETH).&lt;h3&gt;KeyHunt-Cuda (2021)&lt;/h3&gt;&lt;p&gt;Порт логики KeyHunt + VanitySearch на CUDA от &lt;a href=https://github.com/Qalander/&gt;Qalander&lt;/a&gt;. Долгое время держался в топе по CUDA-производительности.&lt;h3&gt;Cyclone (2024)&lt;/h3&gt;&lt;p&gt;Современная CPU-перебиралка от &lt;a class=mention href=https://habr.com/users/dookoo2&gt;@dookoo2&lt;/a&gt;. Некоторым эта программа знакома из комментариев к моей статье &lt;a href=https://habr.com/ru/companies/ruvds/articles/934002/&gt;«Головоломка на 1000 BTC»&lt;/a&gt; — там автор объясняет принятые им архитектурные решения. За точку отсчёта в наших таблицах берётся именно Cyclone.&lt;h3&gt;ecloop (2024)&lt;/h3&gt;&lt;p&gt;Минималистичный C-проект от русскоязычного разработчика &lt;a href=https://github.com/vladkens&gt;vladkens&lt;/a&gt;: &lt;code&gt;main.c&lt;/code&gt; + несколько библиотек для хэшей, минимум зависимостей.&lt;h3&gt;CUDACyclone (2025)&lt;/h3&gt;&lt;p&gt;CUDA-версия Cyclone от того же &lt;a class=mention href=https://habr.com/users/dookoo2&gt;@dookoo2&lt;/a&gt;.&lt;h3&gt;BtcMole (2025)&lt;/h3&gt;&lt;p&gt;Свежий проект от автора под ником &lt;a href=https://github.com/keymole&gt;keymole&lt;/a&gt;. Распространяется в виде бинарных сборок. Поддерживает Windows, Linux; процессоры amd64, arm64, arm; видеокарты Nvidia, AMD.&lt;h2&gt;Методика бенчмаркинга&lt;/h2&gt;&lt;h3&gt;Почему нельзя верить «попугаям»&lt;/h3&gt;&lt;p&gt;Каждая из этих программ во время работы рисует на экране собственную скорость в &lt;code&gt;Mkey/s&lt;/code&gt; (миллионах ключей в секунду). Доверять этим цифрам нельзя по нескольким причинам:&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Скам в крипте — обыденность.&lt;/strong&gt; Цифру можно нарисовать любую.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Холодный VS горячий старт.&lt;/strong&gt; Первые несколько секунд GPU/CPU показывают результат заметно выше среднего рабочего.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Внутренние счётчики бывают просто кривые.&lt;/strong&gt;&lt;/ol&gt;&lt;p&gt;В моём бенчмарке замеряется только время от запуска процесса до того момента, когда он в поток вывода попал искомый приватный ключ. Никаких внутренних метрик в зачёт.&lt;p&gt;Технически это «реши одну и ту же задачу»: каждой программе даётся одинаковый диапазон и случайный ключ-цель внутри него. Время — по системным часам, факт находки — по поиску известного hex в выводе.&lt;p&gt;&lt;strong&gt;Размер выборки: почему 100 итераций.&lt;/strong&gt; Сначала я делал по 10–20 прогонов на программу. На таких выборках одна «неудачная» позиция ключа могла кардинально перевернуть таблицу: разные программы сканируют диапазон в разном порядке (последовательно, со смещением, случайно), и один и тот же случайный ключ оказывается для одной программы «на старте», а для другой «в самом конце». Один такой выброс при 10 запусках сдвигал среднее на десятки процентов. Мне пришлось увеличить поисковые интервалы примерно вдвое относительно первой версии бенча и поднять число прогонов до 100 на каждую программу — итоговый бенчмарк работал всю ночь. Стандартная ошибка среднего теперь ≈ σ/√100 ≈ σ/10, чего вполне достаточно для статистической достоверности.&lt;h3&gt;Репозиторий бенчмарка&lt;/h3&gt;&lt;p&gt;Скрипт и все патчи &lt;a href=https://github.com/inetstar/btcpuzzle_bench&gt;доступны на GitHub&lt;/a&gt;.&lt;details class=spoiler&gt;&lt;summary&gt;Дизайнерские решения в `bench.py`&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Все программы — с настройками по умолчанию.&lt;/strong&gt; Я сознательно не подбирал размер батча, число потоков, grid/slices и т.п. — предполагаю, что авторы выставляют оптимальные настройки по умолчанию. Точные команды можно посмотреть в скрипте.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Один источник случайности.&lt;/strong&gt; SEED фиксирован (&lt;code&gt;SEED = 42&lt;/code&gt;), так что на повторных запусках набор ключей-целей идентичный — можно честно сравнивать прогон с прогоном.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Per-device параметры.&lt;/strong&gt; Для CPU и CUDA задаются разные интервалы (&lt;code&gt;2^33&lt;/code&gt; и &lt;code&gt;2^35&lt;/code&gt; соответственно) и одинаковое число итераций (100). Для AMDGPU — короткий интервал и мало итераций (см. соответствующий раздел).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Единый разбор вывода.&lt;/strong&gt; Каждой программе сопоставлен &lt;code&gt;success_re&lt;/code&gt; — регулярное выражение, по которому распознаётся «нашёл этот ключ».&lt;li&gt;&lt;p&gt;&lt;strong&gt;Тайм-аут с принудительным завершением.&lt;/strong&gt; Если программа уходит в бесконечный цикл или зависает, через &lt;code&gt;BENCH_TIMEOUT&lt;/code&gt; секунд ей шлётся &lt;code&gt;SIGTERM&lt;/code&gt;, потом &lt;code&gt;SIGKILL&lt;/code&gt;. Итерация засчитывается как FAIL.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Изоляция подпрограмм.&lt;/strong&gt; Каждая программа клонируется и собирается в отдельной папке &lt;code&gt;work/&amp;lt;name&amp;gt;/&lt;/code&gt; (кроме BtcMole — cpu/cuda/amdgpu делят одну папку, так как репозиторий один на все версии).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Патчи под сборку.&lt;/strong&gt; Часть программ из обзора без правок просто не собирается на свежем GCC/CUDA/ROCm. Для таких случаев в бенче лежат собственные патчи (bug-fix Makefile’ов, замены deprecated CUDA-API, правки include’ов), которые применяются перед сборкой.&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;p&gt;Запуск: &lt;code&gt;./bench.py --device cpu&lt;/code&gt;, &lt;code&gt;./bench.py --device cuda&lt;/code&gt; или &lt;code&gt;./bench.py --device amdgpu&lt;/code&gt;. Перед первым запуском — &lt;code&gt;--prepare&lt;/code&gt; (clone + build). Все настройки (число итераций, прогревов, размер интервала, seed) — вверху скрипта.&lt;p&gt;Сам скрипт &lt;code&gt;bench.py&lt;/code&gt; написан &lt;a href=https://habr.com/ru/companies/ruvds/articles/1020062/&gt;с помощью Claude Code&lt;/a&gt;.&lt;h3&gt;Оборудование стенда&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;CPU:&lt;/strong&gt; AMD EPYC 7C13 (64 ядра / 128 потоков, 2.45 GHz base)&lt;li&gt;&lt;p&gt;&lt;strong&gt;GPU:&lt;/strong&gt; NVIDIA CMP 90HX (Ampere, sm_86, 50 SM, 1530 MHz @ 200 W power-limit)&lt;li&gt;&lt;p&gt;&lt;strong&gt;AMDGPU:&lt;/strong&gt; Radeon R9 Fury (gfx803, 56 CUs)&lt;li&gt;&lt;p&gt;&lt;strong&gt;OS:&lt;/strong&gt; Linux (Calculate Linux, kernel 6.18)&lt;/ul&gt;&lt;h2&gt;Тестирование CPU-программ&lt;/h2&gt;&lt;h3&gt;Особенности тестирования на CPU&lt;/h3&gt;&lt;p&gt;Эти программы рассчитаны на круглосуточную работу — в реальности процессор нагрет, может сбрасывать частоту. Холодный замер дал бы завышенные цифры. Поэтому перед каждой программой идёт прогревочный прогон, результаты которого не учитываются (константа &lt;code&gt;WARMUP_PER_DEVICE&lt;/code&gt; в &lt;code&gt;bench.py&lt;/code&gt;).&lt;p&gt;&lt;strong&gt;Сколько потоков отдаётся программе.&lt;/strong&gt; Каждой CPU-программе передаётся &lt;strong&gt;115 потоков из 128 доступных&lt;/strong&gt; (~90%). Оставшиеся 13 потоков остаются за графической оболочкой, фоновыми службами, самим &lt;code&gt;bench.py&lt;/code&gt; и т.п. Если отдать программе все 128, она будет конкурировать за процессорное время с GUI и системой — итог станет менее точным.&lt;p&gt;&lt;strong&gt;Важная оговорка про SIMD.&lt;/strong&gt; На моей машине нет AVX-512, есть только AVX-2. Поэтому Cyclone собирается и тестируется именно в этом варианте. Бенчмарк ARM64 я тоже не проводил.&lt;h3&gt;Тестируемые программы&lt;/h3&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Год разработки&lt;th&gt;&lt;p align=left&gt;Язык&lt;th&gt;&lt;p align=left&gt;Источник&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;C++&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/albertobsd/keyhunt&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Cyclone&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;C++&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/Dookoo2/Cyclone&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;ecloop&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;C&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/vladkens/ecloop&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole (CPU)&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;?&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/keymole/btcmole&gt;GitHub&lt;/a&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3&gt;Параметры прогона&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Диапазон поиска&lt;/strong&gt;: &lt;code&gt;[0x200000000, 0x3ffffffff]&lt;/code&gt; (size = &lt;code&gt;2^33&lt;/code&gt; ≈ 8.6 миллиарда ключей).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ключи&lt;/strong&gt;: 100 (разные случайные внутри диапазона).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Эталон скорости&lt;/strong&gt;: Cyclone (за 1.00× принят его суммарный результат).&lt;/ul&gt;&lt;h3&gt;Результаты&lt;/h3&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;OK / FAIL&lt;th&gt;&lt;p align=left&gt;Σ время (s)&lt;th&gt;&lt;p align=left&gt;Avg (s)&lt;th&gt;&lt;p align=left&gt;Скорость (× эталон)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;BtcMole&lt;/strong&gt; (CPU)&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;1111.15&lt;td&gt;&lt;p align=left&gt;11.11&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;1.32&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;ecloop&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;1384.45&lt;td&gt;&lt;p align=left&gt;13.84&lt;td&gt;&lt;p align=left&gt;1.06&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Cyclone (AVX-2)&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;1469.36&lt;td&gt;&lt;p align=left&gt;14.69&lt;td&gt;&lt;p align=left&gt;1.00&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;3691.10&lt;td&gt;&lt;p align=left&gt;36.91&lt;td&gt;&lt;p align=left&gt;0.40&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2&gt;Тестирование GPU-программ&lt;/h2&gt;&lt;h3&gt;Особенности тестирования на GPU&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Холодная карта ≠ рабочая карта&lt;/strong&gt;: на холодной частота держится на разгонной, после прогрева начинаются сброс частот (так называемый тротлинг — защита от перегрева). Любитель публиковать рекорды просто запустит свою программу первой. Чтобы убрать этот эффект, частота и power limit GPU зафиксированы:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;CMP 90HX (NVIDIA)&lt;/strong&gt;: &lt;code&gt;power-limit 200 W&lt;/code&gt;, &lt;code&gt;clock 1530 MHz&lt;/code&gt; — при этих параметрах частота не сбрасывается, сколько бы карта ни работала. Откуда взялись именно такие значения — подробно разобрано в моей предыдущей статье &lt;a href=https://habr.com/ru/companies/ruvds/articles/1017150/&gt;«Максимально выгодно используем видеокарты с помощью школьной формулы из физики»&lt;/a&gt;.&lt;/ul&gt;&lt;p&gt;Плюс 2 прогревочных прогона перед общим тестированием, как и на CPU.&lt;h4&gt;Что заявляют сами авторы&lt;/h4&gt;&lt;p&gt;Заявленные скорости на топовой RTX 5090 (Blackwell, sm_120):&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;CycloneCUDA&lt;/strong&gt; (dookoo2) — &lt;strong&gt;6200 Mkey/s&lt;/strong&gt;.&lt;li&gt;&lt;p&gt;&lt;strong&gt;BtcMole&lt;/strong&gt; (keymole) — &lt;strong&gt;9520 Mkey/s&lt;/strong&gt;.&lt;/ul&gt;&lt;p&gt;Значения приведены для справки, в тестах мы на них не ориентируемся.&lt;h3&gt;Карты Nvidia&lt;/h3&gt;&lt;h4&gt;Тестируемые программы&lt;/h4&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Год разработки&lt;th&gt;&lt;p align=left&gt;Язык&lt;th&gt;&lt;p align=left&gt;Источник&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BitCrack (CUDA)&lt;td&gt;&lt;p align=left&gt;2018&lt;td&gt;&lt;p align=left&gt;C++ / CUDA&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/brichard19/BitCrack&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt-Cuda&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;C++ / CUDA&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/Qalander/KeyHunt-Cuda&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;CUDACyclone&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;CUDA&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/Dookoo2/CUDACyclone&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole (CUDA)&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;закрытый&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/keymole/btcmole&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyKiller-Cuda&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;C++ / CUDA&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://gitlab.com/8891689/keykiller-cuda&gt;GitLab&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyScanner&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;C++ / CUDA&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/graffitilogic/KeyScanner&gt;GitHub&lt;/a&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4&gt;Параметры прогона&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Диапазон поиска&lt;/strong&gt;: &lt;code&gt;[0x800000000, 0xfffffffff]&lt;/code&gt; (size = &lt;code&gt;2^35&lt;/code&gt; ≈ 34.4 миллиарда ключей).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ключи&lt;/strong&gt;: 100.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Эталон скорости&lt;/strong&gt;: CUDACyclone (за 1.00× принят его суммарный результат).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Карта&lt;/strong&gt;: NVIDIA CMP 90HX (sm_86, 50 SM, 200 W @ 1530 MHz).&lt;/ul&gt;&lt;h4&gt;Результаты&lt;/h4&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;OK / FAIL&lt;th&gt;&lt;p align=left&gt;Σ время (s)&lt;th&gt;&lt;p align=left&gt;Avg (s)&lt;th&gt;&lt;p align=left&gt;Скорость (× эталон)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;BtcMole&lt;/strong&gt; (CUDA)&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;901.99&lt;td&gt;&lt;p align=left&gt;9.02&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;1.46&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt-Cuda&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;1302.08&lt;td&gt;&lt;p align=left&gt;13.02&lt;td&gt;&lt;p align=left&gt;1.01&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;CUDACyclone&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;1320.70&lt;td&gt;&lt;p align=left&gt;13.21&lt;td&gt;&lt;p align=left&gt;1.00&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyKiller-Cuda&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;2952.25&lt;td&gt;&lt;p align=left&gt;29.52&lt;td&gt;&lt;p align=left&gt;0.45&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyScanner&lt;td&gt;&lt;p align=left&gt;92 / 8&lt;td&gt;&lt;p align=left&gt;6166.90&lt;td&gt;&lt;p align=left&gt;67.03&lt;td&gt;&lt;p align=left&gt;0.21&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BitCrack (CUDA)&lt;td&gt;&lt;p align=left&gt;100 / 0&lt;td&gt;&lt;p align=left&gt;6289.19&lt;td&gt;&lt;p align=left&gt;62.89&lt;td&gt;&lt;p align=left&gt;0.21&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3&gt;Карты AMD&lt;/h3&gt;&lt;p&gt;Единственная программа, авторы которой заморочились с поддержкой AMD-карт — это BtcMole. Остальные авторы живут в парадигме «есть только NVIDIA».&lt;p&gt;Да, есть ещё clBitCrack (OpenCL), внешне работающая под AMD, но в реальности она совсем не ищет ключи. Она сдулась на первом же ключе из бенча: диапазон &lt;code&gt;0x40000000:0x7fffffff&lt;/code&gt; (всего 2³⁰ ≈ 1.07 млрд ключей), цель — адрес &lt;code&gt;1Q4U2cBxgduTyEiWFWBF16tjsb4hR7zee9&lt;/code&gt;, известный приватный ключ &lt;code&gt;0x6d06ef13&lt;/code&gt;. BtcMole-AMDGPU нашёл ключ за секунды. clBitCrack прокрутил весь диапазон и завершился сообщением &lt;code&gt;Reached end of keyspace&lt;/code&gt; — ключ не найден. То же самое — на остальных трёх итерациях. Похоже, в OpenCL-ядре BitCrack есть баг арифметики или хеширования. Поэтому скрипт пометил все запуски clBitCrack как &lt;code&gt;FAIL&lt;/code&gt;.&lt;p&gt;&lt;strong&gt;Грубая оценка скорости clBitCrack.&lt;/strong&gt; На независимом тесте на AMD RX 580 clBitCrack показывает &lt;strong&gt;~9.42 Mkey/s&lt;/strong&gt;, btcmole-AMDGPU на той же карте — &lt;strong&gt;~240 Mkey/s&lt;/strong&gt;. То есть даже если бы clBitCrack находил ключи, отрыв был бы в &lt;strong&gt;25 раз&lt;/strong&gt; в пользу BtcMole. Это уже не «оптимизация», а просто другая весовая категория.&lt;h4&gt;Тестируемые программы&lt;/h4&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Год разработки&lt;th&gt;&lt;p align=left&gt;Язык&lt;th&gt;&lt;p align=left&gt;Источник&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BitCrack (OpenCL)&lt;td&gt;&lt;p align=left&gt;2018&lt;td&gt;&lt;p align=left&gt;C++ / OpenCL&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/brichard19/BitCrack&gt;GitHub&lt;/a&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole (AMDGPU)&lt;td&gt;&lt;p align=left&gt;2025&lt;td&gt;&lt;p align=left&gt;закрытый&lt;td&gt;&lt;p align=left&gt;&lt;a href=https://github.com/keymole/btcmole&gt;GitHub&lt;/a&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4&gt;Параметры прогона&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Диапазон поиска&lt;/strong&gt;: &lt;code&gt;[0x40000000, 0x7fffffff]&lt;/code&gt; (size = &lt;code&gt;2^30&lt;/code&gt; ≈ 1.07 миллиарда ключей) — короткий, потому что BitCrack-OpenCL заведомо FAIL и тратить на него больше времени смысла нет.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ключи&lt;/strong&gt;: 4 — достаточно для проверки работоспособности BtcMole-AMDGPU, не претендует на статистическую значимость.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Карта&lt;/strong&gt;: AMD Radeon RX 580.&lt;/ul&gt;&lt;h4&gt;Результаты&lt;/h4&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;OK / FAIL&lt;th&gt;&lt;p align=left&gt;Σ время (s)&lt;th&gt;&lt;p align=left&gt;Avg (s)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole (AMDGPU)&lt;td&gt;&lt;p align=left&gt;4 / 0&lt;td&gt;&lt;p align=left&gt;16.73&lt;td&gt;&lt;p align=left&gt;4.18&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BitCrack (OpenCL)&lt;td&gt;&lt;p align=left&gt;0 / 4&lt;td&gt;&lt;p align=left&gt;—&lt;td&gt;&lt;p align=left&gt;—&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Если кто-то знает другие живые программы-решатели для карт AMD — сообщите мне.&lt;h2&gt;Что используют авторы для ускорения перебора&lt;/h2&gt;&lt;p&gt;Все рассматриваемые проекты решают одну и ту же задачу — массовое сложение точек на кривой secp256k1, хеширование SHA-256 + RIPEMD-160, сравнение хэша с целевым. Ниже — основные технические приёмы, упорядоченные по программам.&lt;h3&gt;BitCrack — «в лоб», без массовой инверсии&lt;/h3&gt;&lt;p&gt;Старейшина. Считает каждое сложение точек отдельно. На каждом шаге — инверсия по модулю (&lt;code&gt;InvMod&lt;/code&gt; в конечном поле). Это самая дорогая операция в EC-арифметике (в ~60 раз медленнее умножения). VanitySearch и все последующие снижают эту цену через одну инверсию на большой батч точек (массовая инверсия) — отсюда и пятикратное отставание BitCrack.&lt;h3&gt;VanitySearch — массовая инверсия Монтгомери&lt;/h3&gt;&lt;p&gt;Здесь нужно отдельно сказать спасибо &lt;a href=https://github.com/JeanLucPons&gt;Жан-Люку Понсу&lt;/a&gt; (&lt;code&gt;JeanLucPons&lt;/code&gt; на GitHub). Его проект VanitySearch 2019 года — это, без преувеличения, фундамент всей программ-решателей. По моим оценкам, &lt;strong&gt;больше 90% всех CPU- и GPU-программ для перебора приватных ключей&lt;/strong&gt; так или иначе содержат куски его C+±кода: классы &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;IntGroup&lt;/code&gt;, &lt;code&gt;IntMod&lt;/code&gt;, &lt;code&gt;Point&lt;/code&gt;, &lt;code&gt;SECP256K1&lt;/code&gt;, файлы &lt;code&gt;Random.cpp&lt;/code&gt;, базовые PTX-макросы для 256-битной арифметики. Это видно невооружённым глазом — открываешь репозиторий любого «нового» решателя, а внутри лежит копия этих файлов с минимальными правками. KeyHunt, KeyHunt-Cuda, KeyKiller, KeyScanner, Cyclone — все они стоят на плечах этого человека.&lt;p&gt;Ценность работы Понса не в одной идее, а в их сочетании: он принёс в нишу высокопроизводительный 256-битный классы &lt;code&gt;Int&lt;/code&gt; и &lt;code&gt;IntMod&lt;/code&gt; для 256-битной арифметики, компактную реализацию EC-арифметики над secp256k1 без зависимости от тяжёлого OpenSSL/libsecp256k1. Запрограммировал на C++ передовой алгоритм инверсии DivStep62, Монтгомери-батчи для обработки точек.&lt;p&gt;Идея массовой инверсии: копим произведение разностей &lt;code&gt;(x_i − x_target)&lt;/code&gt;, делаем единственный &lt;code&gt;InvMod&lt;/code&gt; от полного произведения, потом «раскручиваем» обратно, получая инверсию каждой разности по очереди. Эффективная стоимость самой дорогой операции в EC-арифметике падает до 3 умножений на точку. Эту схему сейчас использует &lt;strong&gt;каждая&lt;/strong&gt; CUDA-программа из обзора, кроме BitCrack — и каждая вторая CPU-программа.&lt;p&gt;То, что один человек проделал такую колоссальную работу и выложил её под свободной лицензией, — большое везение для всех. Без VanitySearch не было бы ни KeyHunt-Cuda, ни Cyclone, ни большинства проектов, которые мы здесь сравниваем.&lt;h3&gt;KeyHunt — гибкость и BSGS&lt;/h3&gt;&lt;p&gt;KeyHunt берёт код secp256k1 от Жан-Люка Понса, но накручивает поверх Bloom-фильтр (для проверки по большому списку адресов) и BSGS (для поиска по известному pubkey). Никаких AVX, никакой массовой инверсии в основных режимах перебора. Зато умеет: BTC + ETH, P2PKH + P2SH + bech32, файлы со списками целей. Сила этой программы в алгоритме BabyStep-GiantStep (работает с известными публичными ключами), в простом брутфорсе P2PKH она работает хорошо, но отстаёт от лидеров.&lt;h3&gt;KeyHunt-Cuda и CUDACyclone — inline PTX&lt;/h3&gt;&lt;p&gt;На первый взгляд — там C++ код, но если присмотреться, то многие макросы разворачиваются в inline PTX-ассемблер для 256-битной арифметики:&lt;pre&gt;&lt;code class=cpp&gt;#define UADDO(c, a, b) asm volatile (&amp;#34;add.cc.u64 %0, %1, %2;&amp;#34; : &amp;#34;=l&amp;#34;(c) : &amp;#34;l&amp;#34;(a), &amp;#34;l&amp;#34;(b) : &amp;#34;memory&amp;#34;);&#xA;#define UADDC(c, a, b) asm volatile (&amp;#34;addc.cc.u64 %0, %1, %2;&amp;#34; : &amp;#34;=l&amp;#34;(c) : &amp;#34;l&amp;#34;(a), &amp;#34;l&amp;#34;(b) : &amp;#34;memory&amp;#34;);&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;CUDACyclone дополнительно реализует батчевую инверсию — тот же приём массовой инверсии Монтгомери, перенесённый в ядро. По умолчанию: батч в 128 точек.&lt;h3&gt;Cyclone — AVX-2 интринсики для хешей&lt;/h3&gt;&lt;p&gt;Cyclone использует &lt;code&gt;__m256i&lt;/code&gt; напрямую: SHA-256 и RIPEMD-160 для 8 параллельных потоков сжимаются в одно 256-битное регистр-поле. Файлы &lt;code&gt;sha256_avx2.cpp&lt;/code&gt; и &lt;code&gt;ripemd160_avx2.cpp&lt;/code&gt; — почти учебник по SIMD-хешированию:&lt;pre&gt;&lt;code class=cpp&gt;#define Maj(x, y, z) _mm256_or_si256(_mm256_and_si256(x, y), _mm256_and_si256(z, _mm256_or_si256(x, y)))&#xA;#define Ch(x, y, z)  _mm256_xor_si256(_mm256_and_si256(x, y), _mm256_andnot_si256(x, z))&#xA;#define ROR(x, n)    _mm256_or_si256(_mm256_srli_epi32(x, n), _mm256_slli_epi32(x, 32 - n))&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Модулярная арифметика — на стандартных C+±операторах + наработки Жана-Люка. Есть отдельный AVX-512-вариант для процессоров с поддержкой AVX-512.&lt;h3&gt;ecloop — компиляторные builtin’ы + AVX2&lt;/h3&gt;&lt;p&gt;ecloop сознательно отказался от inline-ассемблера, но взял AVX2-интринсики. 256-битная арифметика — через GCC/Clang-builtin’ы:&lt;pre&gt;&lt;code&gt;__builtin_addcll(x, y, carryin, &amp;amp;carryout)   // add-with-carry&#xA;__builtin_uaddll_overflow(x, y, &amp;amp;rs)         // overflow detection&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Расчёт на то, что современный clang сам поднимет это в &lt;code&gt;adc&lt;/code&gt;/&lt;code&gt;adcx&lt;/code&gt;-инструкции (вместо ручного inline-asm).&lt;p&gt;Хеши — полноценный AVX2 и AVX-512 в &lt;code&gt;lib/rmd160s.c&lt;/code&gt;: SHA-256 и RIPEMD-160 параллельно по 8 потоков через &lt;code&gt;__m256i&lt;/code&gt; и т.п. Размер батча для инверсии — 2048 точек. Итог — 1.06× от Cyclone при компактном коде.&lt;h3&gt;BtcMole — возможные оптимизации&lt;/h3&gt;&lt;p&gt;С BtcMole сложнее — исходники закрыты, все суждения только по бинарникам. Что можно утверждать наверняка:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Аппаратная поддержка x86-64 + ARM64&lt;/strong&gt; — обычно достигается компиляторными SIMD-интринсиками (&lt;code&gt;__m256i&lt;/code&gt;/&lt;code&gt;__m512i&lt;/code&gt; под Intel, &lt;code&gt;vqaddq_*&lt;/code&gt;/&lt;code&gt;vmlal_*&lt;/code&gt; под NEON), либо отдельными asm-вставками для каждой архитектуры. Раз скорость на x86 опережает Cyclone, велика вероятность использования более подходящих интрисинков и ручных asm-вставок.&lt;li&gt;&lt;p&gt;&lt;strong&gt;CUDA-сборка единая для sm_50…sm_120&lt;/strong&gt; — это Fat Binary с заранее скомпилированными &lt;code&gt;.cubin&lt;/code&gt;-ами. Внутри почти наверняка inline PTX (как у конкурентов) или даже SASS (если автор пошёл дорогой Retired Coder).&lt;li&gt;&lt;p&gt;&lt;strong&gt;AMDGPU-сборка покрывает GCN 3-4, CDNA, RDNA1-4&lt;/strong&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Массовая инверсия Монтгомери&lt;/strong&gt; — с такой скоростью без неё нельзя; вопрос только в размере группы.&lt;/ul&gt;&lt;h3&gt;Fat Binary — почему некоторые программы такие большие&lt;/h3&gt;&lt;p&gt;Технология &lt;strong&gt;Fat Binary&lt;/strong&gt; в CUDA-мире — это упаковка нескольких прекомпилированных &lt;code&gt;.cubin&lt;/code&gt;-ов под разные compute capabilities в один исполняемый файл, плюс PTX-код для архитектур, для которых нет предкомпилированного ядра. Преимущество: один бинарник работает «и на 1080Ti, и на RTX 4090, и на новых Blackwell без перекомпиляции». Недостаток — размер.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Размер бинарника&lt;th&gt;&lt;p align=left&gt;Архитектуры в FatBin&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt-Cuda&lt;td&gt;&lt;p align=left&gt;~1.5 МБ&lt;td&gt;&lt;p align=left&gt;одна (выбирается через &lt;code&gt;make ccap=86&lt;/code&gt;)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;CUDACyclone&lt;td&gt;&lt;p align=left&gt;~2.6 МБ&lt;td&gt;&lt;p align=left&gt;три (sm_75, sm_86, sm_89)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole (GPU)&lt;td&gt;&lt;p align=left&gt;~16 МБ&lt;td&gt;&lt;p align=left&gt;11 NVIDIA + ~10 AMD&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;CycloneCUDA и BtcMole заработали без всяких патчей. KeyHunt-Cuda требует ручной сборки под целевую архитектуру (&lt;code&gt;make ccap=89&lt;/code&gt;) — для пользователя без NVCC это барьер.&lt;h3&gt;Сводка по стеку оптимизаций&lt;/h3&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Mass-inverse&lt;th&gt;&lt;p align=left&gt;Inline asm&lt;th&gt;&lt;p align=left&gt;SIMD-хеши&lt;th&gt;&lt;p align=left&gt;Fat Binary&lt;th&gt;&lt;p align=left&gt;Особое&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BitCrack&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;—&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;VanitySearch&lt;td&gt;&lt;p align=left&gt;✅ (родоначальник)&lt;td&gt;&lt;p align=left&gt;PTX&lt;td&gt;&lt;p align=left&gt;—&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;—&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt&lt;td&gt;&lt;p align=left&gt;в спец-режимах&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;BSGS, Bloom&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt-Cuda&lt;td&gt;&lt;p align=left&gt;✅&lt;td&gt;&lt;p align=left&gt;PTX&lt;td&gt;&lt;p align=left&gt;—&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;—&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Cyclone&lt;td&gt;&lt;p align=left&gt;✅&lt;td&gt;&lt;p align=left&gt;❌&lt;td&gt;&lt;p align=left&gt;AVX2/512&lt;td&gt;&lt;p align=left&gt;n/a&lt;td&gt;&lt;p align=left&gt;—&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;ecloop&lt;td&gt;&lt;p align=left&gt;✅ (2048)&lt;td&gt;&lt;p align=left&gt;builtin’ы&lt;td&gt;&lt;p align=left&gt;AVX2&lt;td&gt;&lt;p align=left&gt;n/a&lt;td&gt;&lt;p align=left&gt;компактность&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;CUDACyclone&lt;td&gt;&lt;p align=left&gt;✅ (один-на-батч)&lt;td&gt;&lt;p align=left&gt;PTX&lt;td&gt;&lt;p align=left&gt;—&lt;td&gt;&lt;p align=left&gt;✅ (3 arch)&lt;td&gt;&lt;p align=left&gt;настраиваемые параметры запуска&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole&lt;td&gt;&lt;p align=left&gt;✅&lt;td&gt;&lt;p align=left&gt;предположительно да&lt;td&gt;&lt;p align=left&gt;предположительно да&lt;td&gt;&lt;p align=left&gt;✅ (~20 arch)&lt;td&gt;&lt;p align=left&gt;мульти-платформа CPU+CUDA+AMDGPU&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2&gt;Двигаемся к потолку производительности&lt;/h2&gt;&lt;h3&gt;Retired Coder и проект RCAsm&lt;/h3&gt;&lt;p&gt;Разработчик под ником &lt;strong&gt;Retired Coder (RC)&lt;/strong&gt; известен как автор серии быстрых решателей биткойн-головоломок по алгоритму кенгуру Полларда. На днях он опубликовал проект &lt;strong&gt;RCAsm&lt;/strong&gt; — DSL-обёртку над cuAssembler для написания CUDA-ядер прямо на уровне SASS, без участия PTX и nvcc.&lt;p&gt;Зачем это нужно? Цитирую RC из README:&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;PTX is not powerful enough:&lt;/em&gt; &lt;em&gt;1. You still cannot control registers usage.&lt;/em&gt; &lt;em&gt;2. PTX does not provide all instructions, some of them can be really important.&lt;/em&gt; &lt;em&gt;3. There is no way to declare fast functions.&lt;/em&gt; &lt;em&gt;4. There is no way to use uniform registers and instructions directly.&lt;/em&gt; &lt;em&gt;5. There is no way to specify control codes.&lt;/em&gt; &lt;em&gt;6. There is no good management for carry flags.&lt;/em&gt; &lt;em&gt;7. You have to check what SASS is generated every time.&lt;/em&gt;&lt;/blockquote&gt;&lt;p&gt;PTX — это промежуточное представление, которое NVIDIA-компилятор &lt;code&gt;ptxas&lt;/code&gt; потом транслирует в реальный машинный код для GPU (SASS). На многих этапах ptxas принимает решения «за разработчика» в ущерб эффективности.&lt;p&gt;RCAsm обходит это, давая прямой доступ к SASS ценой головной боли — синтаксис SASS намного ужаснее обычного ассемблера.&lt;p&gt;Под капотом — модифицированный &lt;a href=https://github.com/cloudcores/CuAssembler&gt;cuAssembler&lt;/a&gt; и Python-обёртка. Требуется CUDA v12.8 (другие версии не поддерживаются).&lt;p&gt;Работает хакерским образом — вначале пишется обычное ядро, компилируется, а потом патчится.&lt;p&gt;Это и есть последнее слово индустрии для перебора приватных ключей. Если кто-то решит побить рекорды Retired Coder (19 Mkey/s на 5090 в алгоритме кенгуру Полларда), наверное, ему придётся идти тем же путём.&lt;h2&gt;Команды для индивидуального поиска&lt;/h2&gt;&lt;p&gt;Если хочется попробовать всё-таки вскрыть нерешённую головоломку — это для тех, кто верит в удачу или у кого куча собственного железа (см. ниже про математику лотереи). Команды ниже — для головоломки &lt;strong&gt;№ 71&lt;/strong&gt; (BTC-адрес &lt;code&gt;1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU&lt;/code&gt;, диапазон &lt;code&gt;2^70..2^71-1&lt;/code&gt;). Подставьте свой кошелёк/диапазон по аналогии для других номеров.&lt;h3&gt;CPU&lt;/h3&gt;&lt;pre&gt;&lt;code class=bash&gt;# Cyclone (AVX2)&#xA;./Cyclone -t 24 -r 400000000000000000:7fffffffffffffffff \&#xA;  -a 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU&#xA;&#xA;# ecloop — режим mul (умножение точек)&#xA;./ecloop mul -t 24 -f target.h160 \&#xA;  -r 400000000000000000:7fffffffffffffffff&#xA;&#xA;# keyhunt — последовательный режим по адресу&#xA;./keyhunt -m address -t 24 -l c \&#xA;  -r 400000000000000000:7fffffffffffffffff \&#xA;  -a 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU&#xA;&#xA;# BtcMole — единый бинарник для всех устройств&#xA;./bm bf -pz 71 +cpu              # только CPU&#xA;./bm bf -pz 71 +cpu:50%          # 50% ядер&#xA;./bm bf --address 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU \&#xA;        --range 400000000000000000:7fffffffffffffffff +cpu&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;CUDA&lt;/h3&gt;&lt;h4&gt;CUDACyclone&lt;/h4&gt;&lt;pre&gt;&lt;code class=bash&gt;./CUDACyclone --address 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU \&#xA;  --range 400000000000000000:7fffffffffffffffff \&#xA;  --grid 128,8 --slices 64&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;KeyHunt-Cuda&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;./KeyHunt -t 0 -g --gpui 0 --gpux 256,256 \&#xA;  -m address -l c \&#xA;  -r 400000000000000000:7fffffffffffffffff \&#xA;  1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;KeyKiller-Cuda — принимает только размер диапазона в битах&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;./kk -r 71 -a 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;KeyScanner&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;./keyscanner -a 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU \&#xA;  -r 400000000000000000:7fffffffffffffffff&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;BitCrack (CUDA)&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;./cuBitCrack --keyspace 400000000000000000:7fffffffffffffffff \&#xA;  -o found.txt 1PWo3JeB9jrGwfHDNpdGK54CRas7fsVzXU&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;BtcMole — CUDA с тонкой настройкой числа SM&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;./bm bf -pz 71 -cpu +cuda            # все SM&#xA;./bm bf -pz 71 -cpu +cuda:30,40      # 30 SM на карте 0, 40 на карте 1&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;AMDGPU&lt;/h3&gt;&lt;p&gt;BtcMole — единственный реально работающий вариант на картах AMD.&lt;pre&gt;&lt;code&gt;./bm bf -pz 71 -cpu -cuda +amdgpu&#xA;# Оставляем 20% GPU для интерфейса и роликов&#xA;./bm bf -pz 71 -cpu -cuda +amdgpu:80%&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;Сохранение прогресса между рестартами&lt;/h3&gt;&lt;p&gt;После проверки CLI каждой программы и кода — поддержка чек-поинтов есть у большего числа проектов, чем казалось:&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Программа&lt;th&gt;&lt;p align=left&gt;Сохраняет прогресс с возможностью продолжения&lt;th&gt;&lt;p align=left&gt;Как включить&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BitCrack&lt;td&gt;&lt;p align=left&gt;да&lt;td&gt;&lt;p align=left&gt;&lt;code&gt;--continue FILE&lt;/code&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;BtcMole&lt;td&gt;&lt;p align=left&gt;да, для каждого устройства при диапазонах размером в 2^40 ключей и выше&lt;td&gt;&lt;p align=left&gt;автоматически подхватывается&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Cyclone (CPU)&lt;td&gt;&lt;p align=left&gt;пишет log в &lt;code&gt;progress.txt&lt;/code&gt; каждые 5 мин, ручное возобновление&lt;td&gt;&lt;p align=left&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyKiller-Cuda&lt;td&gt;&lt;p align=left&gt;да (только в range-режиме, не в random)&lt;td&gt;&lt;p align=left&gt;флаг &lt;code&gt;-b&lt;/code&gt; («backup mode»)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyScanner&lt;td&gt;&lt;p align=left&gt;да (только в range-режиме)&lt;td&gt;&lt;p align=left&gt;&lt;code&gt;-n N&lt;/code&gt; — чек-поинт каждые N минут&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt (CPU)&lt;td&gt;&lt;p align=left&gt;нет&lt;td&gt;&lt;p align=left&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;KeyHunt-Cuda&lt;td&gt;&lt;p align=left&gt;нет&lt;td&gt;&lt;p align=left&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;CUDACyclone&lt;td&gt;&lt;p align=left&gt;нет&lt;td&gt;&lt;p align=left&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;ecloop&lt;td&gt;&lt;p align=left&gt;нет&lt;td&gt;&lt;p align=left&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2&gt;Вероятность открытия головоломки&lt;/h2&gt;&lt;p&gt;Если у вас нет сотен видеокарт, то вскрытие нерешённой головоломки (№ 71, 72, …) — это лотерея. Победит либо тот, кто сильно раньше начал, либо тот, у кого больше железок.&lt;p&gt;Поясню масштаб. Размер диапазона головоломки № 71 — &lt;code&gt;2^70 ≈ 1.18 × 10^21&lt;/code&gt; ключей. Производительность одной CMP 90HX в btcmole-cuda ≈ 1950 Mkeys/s (среднее по нашему бенчу). Одной картой полный перебор 50% интервала займёт 9 599 лет. То есть если вы 1 год перебираете, то у вас примерно 1 шанс из 19 000, что именно вы вскроете головоломку. Это выше, чем сорвать джекпот в лотерею, но всё равно мало.&lt;h2&gt;Заключение&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Победители:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Среди программ с открытым кодом:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;На CPU — &lt;strong&gt;ecloop&lt;/strong&gt; (1.06× относительно Cyclone). Cyclone почти не уступает (1.00×).&lt;li&gt;&lt;p&gt;На CUDA — &lt;strong&gt;KeyHunt-Cuda&lt;/strong&gt; (1.01×) с минимальным отрывом от CUDACyclone (1.00×); фактически паритет.&lt;/ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Среди всех программ (включая закрытый код):&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;На CPU — &lt;strong&gt;btcmole&lt;/strong&gt; (1.32× относительно Cyclone).&lt;li&gt;&lt;p&gt;На CUDA — &lt;strong&gt;btcmole&lt;/strong&gt; (1.46× относительно CUDACyclone).&lt;li&gt;&lt;p&gt;На AMD GPU — &lt;strong&gt;btcmole&lt;/strong&gt; (единственный, кто работает; остальные либо не пытались поддержать AMD, либо не работают на свежем ROCm).&lt;/ul&gt;&lt;/ul&gt;&lt;p&gt;Конкретные источники преимущества BtcMole проверить нельзя — код закрыт. Но судя по поведению, набор приёмов мало отличается от лидеров с открытым кодом: массовая инверсия Монтгомери, низкоуровневая работа с флагами переноса, толстый Fat Binary под все актуальные железки.&lt;p&gt;&lt;strong&gt;Про внутренние «попугаи».&lt;/strong&gt; В целом, к моему удивлению, заявленные авторами цифры более или менее соответствуют реальности — относительный расклад программ на «их» Mkey/s примерно совпадает с тем, что показывает наш честный wall-clock-бенчмарк. &lt;strong&gt;Единственное яркое исключение — KeyKiller-Cuda&lt;/strong&gt;: там внутренний счётчик завышал результат раза в полтора (учитывалось только GPU-время, без CPU-составляющей), и без патча программа выглядела бы намного сильнее, чем есть. У CUDACyclone цифры в Mkey/s заметно «пляшут» от запуска к запуску, но среднее всё равно близко к реальной скорости. Так что общее правило «не верьте экранным цифрам» сохраняется, но конкретно у этих авторов — без злого умысла.&lt;p&gt;Индустрия не стоит на месте. Retired Coder (человек решивший 120, 125 и 130 головоломки) уже показал, что чистый SASS даёт ещё 15-25 процентов по сравнению с PTX. Правда, по его словам, программироание на SASS несёт серьёзные риски потери психического здоровья.&lt;p&gt;&lt;strong&gt;Про пулы.&lt;/strong&gt; На базе почти каждой программы из списка есть свой пул для совместного перебора. Я &lt;strong&gt;не буду&lt;/strong&gt; брать на себя ответственность кого-то рекомендовать: основатели пулов, как правило, анонимны, проверить их репутацию невозможно, гарантий выплаты вашей доли в случае находки нет.&lt;p&gt;Я прошу прощения у авторов непротестированных программ — я протестировал всё, что нашёл из поиска гугла и телеграм-чатов. Хотя, уверен, что есть масса авторов со своими самописными решениями, которые не попали в обзор.&lt;p&gt;Но комментарии открыты — напишите там о своих программах. Запускайте &lt;a href=https://github.com/inetstar/btcpuzzle_bench&gt;бенчмарк&lt;/a&gt; на своём железе со своими программами, пишите свои результаты.&lt;p&gt;В следующей части (она будет не ранее чем через месяц) мы будем рассматривать программы для решения 135-й и 140-й головоломок. Так что допиливайте ваш софт и присылайте ссылки в личку.&lt;p&gt;Всем цифровым кладоискателям — удачи!&lt;p&gt;© 2026 ООО «МТ ФИНАНС»&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/ruvds/articles/1029952/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029952</guid>
      <pubDate>Thu, 30 Apr 2026 13:00:42 +0000</pubDate>
    </item>
    <item>
      <title>Xcode Simulator — Ускоряем прогон тестов на CI + Fastlane</title>
      <link>https://habr.com/ru/articles/1030150/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030150</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Речь пойдет про наш любимый &lt;code&gt;fastlane&lt;/code&gt;, если вы являетесь специалистом по &lt;a href=https://github.com/MarathonLabs/marathon rel=&#34;noopener noreferrer nofollow&#34;&gt;Maraphon&lt;/a&gt; или &lt;a href=https://github.com/avito-tech/Emcee rel=&#34;noopener noreferrer nofollow&#34;&gt;Emcee&lt;/a&gt;, то, возможно, мои советы для вас окажутся больше вредными / нелепыми / и порой даже забывными - не обессудьте.&lt;p&gt;Но в первую очередь хочу отметить на Хабре две отменные статьи схожие по содержанию от одного и того же автора, очень рекомендую с ними ознакомиться:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/companies/vivid_money/articles/649901/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Ускоряем прохождение iOS UI-тестов. Часть 1. Запуск тестов без сборки проекта&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/companies/vivid_money/articles/652397/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Ускоряем прохождение iOS UI-тестов. Часть 2. Распараллеливание тестов&lt;/a&gt;&lt;/ul&gt;&lt;hr&gt;&lt;p&gt;И вот что я увидел в &lt;code&gt;Fastfile&lt;/code&gt;&lt;p&gt;В &lt;code&gt;Fastfile&lt;/code&gt; ничего не обычного, обычный запуск тестов через &lt;a href=https://docs.fastlane.tools/actions/run_tests/#run_tests rel=&#34;noopener noreferrer nofollow&#34;&gt;scan&lt;/a&gt;. Но для начала опишу испытуемый небольшой проект, для наглядности в виде списка:&lt;ol&gt;&lt;li&gt;&lt;p&gt;iOS проект (Swift + SwiftUI + UIKit + SwiftPM)&lt;li&gt;&lt;p&gt;Отдельная джоба для юнит тестов&lt;li&gt;&lt;p&gt;Отдельная джоба для UI тестов на последней ОС (на момент написания статьи это iOS 26.4.1)&lt;li&gt;&lt;p&gt;Отдельная джоба для UI тестов для iOS 18.6 (UI для iOS 26 и 18 сильно отличается, поэтому тестами покрываются обе версии, чтобы отлавливать регрессии на старых осях)&lt;li&gt;&lt;p&gt;Джобы в пайплайне идут последовательно (для GitLab &lt;code&gt;concurrent = 1&lt;/code&gt;)&lt;li&gt;&lt;p&gt;Испытуемый проект для статьи это не оригинальный рабочий проект, так как оригинальный имеет очень много тестов и кода для сборки, тогда бы исследование для статьи вышло бы в очень долгий процесс, но испытуемый проект очень похож на боевой по структуре и самое важное по структуре тестов и их интеграции на CI&lt;/ol&gt;&lt;p&gt;Вот так выглядят джобы в пайплайне на CI, просто запуск трех видов тестов:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/1d/67/4c/1d674ca81a5a0099ce9b955514786b9c.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/1d/67/4c/1d674ca81a5a0099ce9b955514786b9c.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/1d/67/4c/1d674ca81a5a0099ce9b955514786b9c.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;А &lt;code&gt;fastlane&lt;/code&gt; слудующим образом:&lt;pre&gt;&lt;code class=ruby&gt; private_lane :run_testing do |options|&#xA;    scan(&#xA;        project: project,&#xA;        scheme: options[:scheme],&#xA;        testplan: options[:testplan],&#xA;        destination: &amp;#34;platform=iOS Simulator,id=#{options[:uuid]}&amp;#34;,&#xA;        skip_detect_devices: true,&#xA;        code_coverage: false,&#xA;        derived_data_path: derived_data_path,&#xA;        disable_concurrent_testing: true&#xA;    )&#xA;end&#xA;&#xA;  lane :run_unit_tests do&#xA;    run_testing(scheme: &amp;#34;AppTests&amp;#34;, testplan: &amp;#34;UnitTests&amp;#34;, uuid: &amp;#34;123..&amp;#34;)&#xA;  end&#xA;&#xA;  lane :run_ui_tests_26_4 do&#xA;    run_testing(scheme: &amp;#34;AppUITests&amp;#34;, testplan: &amp;#34;UITests&amp;#34;, uuid: &amp;#34;123..&amp;#34;)&#xA;  end&#xA;&#xA;  lane :run_ui_tests_18_6 do&#xA;    run_testing(scheme: &amp;#34;AppUITests&amp;#34;, testplan: &amp;#34;UITests&amp;#34;, uuid: &amp;#34;456..&amp;#34;)&#xA;  end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Обычный пускатель тестов без излишеств в каждом втором туториале про &lt;code&gt;fastlane&lt;/code&gt;, где под капотом вот такая &lt;code&gt;xcodebuild&lt;/code&gt; команда:&lt;pre&gt;&lt;code class=bash&gt;xcodebuild \&#xA;    -scheme AppTests \&#xA;    -project App.xcodeproj \&#xA;    -derivedDataPath DerivedData/App-biooakmzntobceawoboqmizvekct \&#xA;    -destination &amp;#39;platform=iOS Simulator,id=5F53A860-544D-4A6F-9F1A-C414CC89DDA5&amp;#39; \&#xA;    -disable-concurrent-testing \&#xA;    -enableCodeCoverage NO \&#xA;    -testPlan &amp;#39;AppTests&amp;#39; \&#xA;    build test&#xA;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Запустим тесты и посмотрим на время выполнения:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/23/26/d5/2326d568ecaa52b0069fdab1a5a87bef.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/23/26/d5/2326d568ecaa52b0069fdab1a5a87bef.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/23/26/d5/2326d568ecaa52b0069fdab1a5a87bef.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Суммарное время вышло 11 минут и 4 секунды (запускал пайплайн несколько раз и взял среднее), зафиксируем это время как время отсчета до наших изменений.&lt;p&gt;Как можно видеть, все три джобы каждый раз собирают проект, иногда сборка попадает в кеш и быстро собирается, иногда на ровном месте SwiftPM глючит и начинает пересобирать свои зависимости. Предлагаю выделить отдельную джобу, которая будет один раз собрать исходники в текущем пайплайне для всех тестов и потом раздаст результат сборки тестовым джобам. Посмотрим, будет ли от этого толк.&lt;hr&gt;&lt;h2&gt;Собираем приложение один раз и раздаем тестовым задачам&lt;/h2&gt;&lt;p&gt;Для этого будем использовать новый флаг &lt;code&gt;-testProductsPath&lt;/code&gt; из &lt;a href=https://developer.apple.com/documentation/xcode-release-notes/xcode-13_3-release-notes rel=&#34;noopener noreferrer nofollow&#34;&gt;Xcode 13.3&lt;/a&gt;, который описан следующим образом:&lt;pre&gt;&lt;code&gt;xcodebuild now supports an .xctestproducts bundle format for the build-for-testing and test-without-building actions. Using a bundle makes it easier to run tests, particularly when transporting tests between systems. Use the new -testProductsPath argument to set the path to the bundle.&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Простым языком, для поиска приложения для запуска тестов когда используется флаг &lt;code&gt;build-for-testing&lt;/code&gt; теперь не нужно исследовать &lt;code&gt;DerivedData&lt;/code&gt;, &lt;code&gt;xcodebuild&lt;/code&gt; просто положит финальную сборку в специальную &lt;code&gt;.xctestproducts&lt;/code&gt; директорию, которую потом нужно передать снова через &lt;code&gt;-testProductsPath&lt;/code&gt; + &lt;code&gt;test-without-building&lt;/code&gt; для запуска тестов.&lt;p&gt;&lt;code&gt;Lane&lt;/code&gt; для сборки продукта для тестов будет выглядеть следующим образом:&lt;pre&gt;&lt;code class=ruby&gt;  private_lane :build_for_testing do |options|&#xA;    # создаем `.xctestproducts` прям в DerivedData директории&#xA;    product_path = &amp;#34;#{File.join(derived_data_path, &amp;#34;#{options[:scheme]}&amp;#34;)}.xctestproducts&amp;#34;&#xA;    scan(&#xA;      project: project,&#xA;      scheme: options[:scheme],&#xA;      sdk: &amp;#34;iphonesimulator&amp;#34;,&#xA;      destination: &amp;#34;generic/platform=iOS Simulator&amp;#34;,&#xA;      # Тут важно это выставить `true`&#xA;      build_for_testing: true,&#xA;      code_coverage: false,&#xA;      derived_data_path: derived_data_path,&#xA;      # Так как fastlane не поддерживает `-testProductsPath` передаем через `xcargs`&#xA;      xcargs: &amp;#34;-testProductsPath #{product_path}&amp;#34;&#xA;    )&#xA;&#xA;    # Так как после сборки `.xctestproducts` также содержит артефакты сборки,&#xA;    # то мы удаляем лишнее, так как нам нужно только само приложение и пускатель UI тестов&#xA;    tests_files = [&#xA;      &amp;#34;App.app&amp;#34;,&#xA;      &amp;#34;AppUITests-Runner.app&amp;#34;&#xA;    ]&#xA;    derived_data = File.join(product_path, &amp;#34;Binaries/0/Debug-iphonesimulator&amp;#34;)&#xA;    Dir.children(derived_data).each do |name|&#xA;      if tests_files.any?(name)&#xA;        next&#xA;      else&#xA;        path = &amp;#34;#{derived_data}/#{name}&amp;#34;&#xA;        if File.directory?(path)&#xA;          FileUtils.rm_rf(path)&#xA;        else&#xA;          File.delete(path)&#xA;        end&#xA;      end&#xA;    end&#xA;&#xA;    # Директория артефактов, это директория сборок, которую мы будем передавать&#xA;    # тестовым джобам, которые будут ее передавать в `-testProductsPath` при запуске тестов&#xA;    artifacts_path = File.join(ENV[&amp;#34;HOME&amp;#34;], &amp;#34;builds/artifacts/#{options[:uname]}&amp;#34;)&#xA;    if !File.exist?(artifacts_path)&#xA;      Dir.mkdir(artifacts_path)&#xA;    end&#xA;    # Перемещаем `.xctestproducts` из Xcode DerivedData в артефакты&#xA;    FileUtils.mv(product_path, artifacts_path)&#xA;    # Возвращаем путь до `.xctestproducts` из артефактов&#xA;    File.join(artifacts_path, File.basename(product_path))&#xA;  end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Мы описали &lt;code&gt;lane&lt;/code&gt; который:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Соберет приложение в виде &lt;code&gt;${SCHEME}.xctestproducts&lt;/code&gt; для тестов&lt;li&gt;&lt;p&gt;Поместит &lt;code&gt;${SCHEME}.xctestproducts&lt;/code&gt; в артефакты (специальную папку на CI)&lt;li&gt;&lt;p&gt;Вернет из функции путь до &lt;code&gt;${SCHEME}.xctestproducts&lt;/code&gt; в артефактах&lt;/ol&gt;&lt;p&gt;В моем конкретно примере, этот &lt;code&gt;lane&lt;/code&gt; вернет такого вида путь &lt;code&gt;$HOME/builds/artifacts/2026.04.28_01-44-11-883074-+0700/${SCHEME}.xctestproducts&lt;/code&gt;. Под капотом &lt;code&gt;xcodebuild&lt;/code&gt; комнада для сборки продукта (&lt;code&gt;.xctestproducts&lt;/code&gt;) выглядит так:&lt;pre&gt;&lt;code class=bash&gt;xcodebuild \&#xA;    -scheme AppTests \&#xA;    -project App.xcodeproj \&#xA;    -derivedDataPath DerivedData/App-biooakmzntobceawoboqmizvekct \&#xA;    -sdk &amp;#39;iphonesimulator&amp;#39; \&#xA;    -destination &amp;#39;generic/platform=iOS Simulator&amp;#39; \&#xA;    -enableCodeCoverage NO \&#xA;    -testProductsPath DerivedData/App-biooakmzntobceawoboqmizvekct/AppTests.xctestproducts \&#xA;    build-for-testing&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Теперь напишем &lt;code&gt;lane&lt;/code&gt;, который собирает продукты для юнит тестов и UI тестов:&lt;pre&gt;&lt;code class=ruby&gt;lane :build_tests_products do&#xA;    # Выставляем одинаковое имя, чтобы все продукты этой джобы лежали в одной директории&#xA;    uname = &amp;#34;#{Time.now.strftime(&amp;#34;%Y.%m.%d_%H-%M-%S-%6N-%z&amp;#34;)}&amp;#34;&#xA;    build_for_testing(&#xA;        scheme: &amp;#34;AppTests&amp;#34;,&#xA;        uname: uname&#xA;    )&#xA;    products = build_for_testing(&#xA;        scheme: &amp;#34;AppUITests&amp;#34;,&#xA;        uname: uname&#xA;    )&#xA;    File.write(File.join(project_dir, &amp;#34;products.txt&amp;#34;), File.dirname(products))&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Обратите внимание на последнюю строку:&lt;pre&gt;&lt;code class=ruby&gt;File.write(File.join(project_dir, &amp;#34;products.txt&amp;#34;), File.dirname(products))&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Так как у нас будет отдельная джоба для сборки приложения, то нам затем нужно передать эти данные дочерним тестовым процессам. Идеально это всё делать “по-взрослому”, заархивировать данные и загрузить в &lt;code&gt;artifactory&lt;/code&gt; на &lt;code&gt;aws&lt;/code&gt; например, откуда дочерние джобы себе скачают, что им нужно. Но для тестового примера или когда у вас CI это просто одна физическая машина, то проще результаты сборки сохранить тут же локально и просто передать путь до них через простой текстовый файл который загрузить в тот же &lt;code&gt;artifactory&lt;/code&gt;, при этом нужно загрузить только этот небольшой файл, а не огромный архив, и потом ничего долго скачивать не нужно, так как файлы тестовых сборок уже лежат локально на машине.&lt;p&gt;Поэтому в нашем примере, пишем путь до &lt;code&gt;.xctestproducts&lt;/code&gt; в файл &lt;code&gt;products.txt&lt;/code&gt;, который положим в корень проекта, что будет автоматически скачено дочерней джобой и автоматически станет доступно в джобе тестирования. В &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; это будет выглядеть примерно так:&lt;pre&gt;&lt;code&gt;iOS Build Tests Products:&#xA;  extends: .testing_template&#xA;  script:&#xA;    - bundle exec fastlane ios build_tests_products&#xA;  artifacts:&#xA;    when: on_success&#xA;    paths:&#xA;      - products.txt&#xA;    expire_in: 1 day&#xA;&#xA;iOS 26.4.1 iPhone | UI Tests:&#xA;  extends: .testing_template&#xA;  needs: [ &amp;#34;iOS Build Tests Products&amp;#34; ]&#xA;  script:&#xA;    - bundle exec fastlane ios run_ui_tests_26_4&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Директория со сборками для тестов на CI будет похожа на пример:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/c8/c3/56/c8c356c1415e37f70c933d0d4e795204.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/c8/c3/56/c8c356c1415e37f70c933d0d4e795204.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/c8/c3/56/c8c356c1415e37f70c933d0d4e795204.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;А файл &lt;code&gt;products.txt&lt;/code&gt; имеет следующее содержание:&lt;pre&gt;&lt;code class=bash&gt;cat products.txt &#xA;/Users/user/builds/artifacts/2026.04.28_13-54-47-168741-+0700&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;После коммита этих изменений в репозиторий, наш пайплайн стал выглядеть следующим образом:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/ea/90/5f/ea905fb3a2708afcb9c136f4d2558776.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/ea/90/5f/ea905fb3a2708afcb9c136f4d2558776.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/ea/90/5f/ea905fb3a2708afcb9c136f4d2558776.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;И наконец обновим наш последний &lt;code&gt;lane&lt;/code&gt; с именем &lt;code&gt;run_testing&lt;/code&gt; который запускает тесты, раньше он и собирал приложение и сразу же на нем запускал тесты. Теперь за сборку тестов у нас отвечает отдельный &lt;code&gt;lane&lt;/code&gt; с именем &lt;code&gt;build_tests_products&lt;/code&gt; , поэтому немного поправим &lt;code&gt;lane&lt;/code&gt; запуска тестов чтобы он не собирал приложение, а передавал готовую сборку через &lt;code&gt;-testProductPath&lt;/code&gt;.&lt;pre&gt;&lt;code class=ruby&gt;private_lane :run_testing do |options|&#xA;    products_path = File.read(File.join(project_dir, &amp;#34;products.txt&amp;#34;))&#xA;    product_path = &amp;#34;#{products_path}/#{options[:scheme]}.xctestproducts&amp;#34;&#xA;    scan(&#xA;        testplan: options[:testplan],&#xA;        # Важно передать пустую строку из-за бага https://github.com/fastlane/fastlane/issues/20540&#xA;        package_path: &amp;#34;&amp;#34;,&#xA;        # Важно выключить `build` фазу. Поведение флага:&#xA;        # - когда не skip, то добавляется &amp;#39;build test&amp;#39;&#xA;        # - когда skip, то добавляется &amp;#39;test&amp;#39;&#xA;        skip_build: true,&#xA;        # Выставляем в `true`, чтобы выключить &amp;#39;test&amp;#39; флаг (смотри выше)&#xA;        test_without_building: true,&#xA;        destination: &amp;#34;platform=iOS Simulator,id=#{options[:destination]}&amp;#34;,&#xA;        skip_detect_devices: true,&#xA;        disable_concurrent_testing: true,&#xA;        derived_data_path: product_path,&#xA;        xcargs: &amp;#34;-testProductsPath #{product_path}&amp;#34;&#xA;    )&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Что транслируется в&lt;pre&gt;&lt;code class=bash&gt;xcodebuild \&#xA;    -destination &amp;#39;platform=iOS Simulator,id=4E015A22-1A11-4C64-8A4C-46D2285B0262&amp;#39; \&#xA;    -derivedDataPath /Users/user/builds/artifacts/2026.04.28_22-34-01-946967-+0700/AppTests.xctestproducts \&#xA;    -disable-concurrent-testing \&#xA;    -enableCodeCoverage NO \&#xA;    -testPlan &amp;#39;AppTests&amp;#39; \&#xA;    -testProductsPath /Users/user/builds/artifacts/2026.04.28_22-34-01-946967-+0700/AppTests.xctestproducts \&#xA;    test-without-building&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Запускаем снова весь пайплайн, чтобы проверить, стоило ли это всех наших усилий:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/4e/7b/0e/4e7b0ec889570977785e631209f9ec42.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/4e/7b/0e/4e7b0ec889570977785e631209f9ec42.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/4e/7b/0e/4e7b0ec889570977785e631209f9ec42.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Ожидаемо получаем прирост в 21.7% (8 минут и 40 секунд вместо 11 минут, запускал пайплайн несколько раз и взял среднее), то есть на 1/5 стало быстрее, а это почти две с половиной минуты. В день этот пайплайн разработчиками может запускаться десятки раз, потому что иногда пайплайны запускались не только на создание MR/PR, но и просто на push в ветку. Идем дальше.&lt;hr&gt;&lt;h2&gt;Переиспользуем симуляторы для запуска тестов&lt;/h2&gt;&lt;p&gt;Переходим к следующей оптимизации скорости пайплайна. Приведу часть кода из &lt;code&gt;Fastlane&lt;/code&gt;, что меня заинтересовал:&lt;pre&gt;&lt;code class=ruby&gt;private_lane :run_testing do |options|&#xA;    uuid = (sh &amp;#34;xcrun simctl create #{options[:SimName]} #{options[:SimDeviceType]} #{options[:SimRuntime]}&amp;#34;).chop&#xA;    sh &amp;#34;xcrun simctl boot #{uuid}&amp;#34;&#xA;    sh &amp;#34;xcrun simctl bootstatus #{uuid}&amp;#34;&#xA;    begin&#xA;        scan(&#xA;            destination: &amp;#34;platform=iOS Simulator,id=#{uuid}&amp;#34;,&#xA;            ...&#xA;        )&#xA;    ensure&#xA;        sh &amp;#34;xcrun simctl shutdown #{uuid}&amp;#34;&#xA;        sh &amp;#34;xcrun simctl delete #{uuid}&amp;#34;&#xA;    end&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Это тот самый &lt;code&gt;run_testing&lt;/code&gt; который запускает тесты. Обратите внимание, что на каждый тест этот &lt;code&gt;lane&lt;/code&gt; создает симулятор и после теста удаляет его. Нет никакого намека на использование уже созданных симуляторов. Сейчас объясню, в чем разница. Но сначала выясним что делает каждая команда из кода выше для &lt;code&gt;simctl&lt;/code&gt;. Как вы знаете &lt;code&gt;simctl&lt;/code&gt; позволяет управлять симуляторами. Выше команды делают следующее.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Команда &lt;code&gt;create&lt;/code&gt; создает симулятор:&lt;/ol&gt;&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl create ${SimName} ${SimDeviceType} ${SimRuntime}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol start=2&gt;&lt;li&gt;&lt;p&gt;Команда &lt;code&gt;boot&lt;/code&gt; поднимает симулятор:&lt;/ol&gt;&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl boot 4E015A22-1A11-4C64-8A4C-46D2285B0262&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol start=3&gt;&lt;li&gt;&lt;p&gt;Команда &lt;code&gt;bootstatus&lt;/code&gt; лочит вызывателя до тех пор, пока симулятор не будет готов к работе:&lt;/ol&gt;&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl bootstatus 4E015A22-1A11-4C64-8A4C-46D2285B0262&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Теперь устроим замеры скорости запуска симулятора уже созданного и только что созданного. Командой ниже через &lt;code&gt;time&lt;/code&gt; замеряем общее время трех &lt;code&gt;simctl&lt;/code&gt; команд &lt;code&gt;create&lt;/code&gt; + &lt;code&gt;boot&lt;/code&gt; + &lt;code&gt;bootstatus&lt;/code&gt;, то есть только что созданного симулятора, который сразу же запускаем и ждем, когда он будет готов к использованию:&lt;pre&gt;&lt;code class=bash&gt;time (SIM_ID=$(xcrun simctl create &amp;#34;iPhone-17-Pro-Max-Unit-Tests&amp;#34; &amp;#34;com.apple.CoreSimulator.SimDeviceType.iPhone-17-Pro-Max&amp;#34; &amp;#34;com.apple.CoreSimulator.SimRuntime.iOS-26-4&amp;#34;) \&#xA;    &amp;amp;&amp;amp; xcrun simctl boot ${SIM_ID} \&#xA;    &amp;amp;&amp;amp; xcrun simctl bootstatus ${SIM_ID})&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Результат:&lt;pre&gt;&lt;code class=bash&gt;[2026-04-28 17:43:24 +0000] Status=4, isTerminal=NO, Elapsed=00:29.&#xA;&#x9;Waiting on System App&#xA;&#xA;[2026-04-28 17:43:25 +0000] Status=4, isTerminal=NO, Elapsed=00:30.&#xA;&#x9;Waiting on System App&#xA;&#xA;[2026-04-28 17:43:26 +0000] Status=4294967295, isTerminal=YES, Elapsed=00:31.&#xA;&#x9;Finished&#xA;&#xA;0.08s user 0.09s system 0% cpu 32.036 total&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;То есть процесс подготовки симулятора к тестам занимает 32 секунды. Теперь то же самое, только пропускаем шаг создания симулятора:&lt;pre&gt;&lt;code class=bash&gt;time (xcrun simctl boot ${SIM_ID} &amp;amp;&amp;amp; xcrun simctl bootstatus ${SIM_ID})&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Результат в 3 раза быстрее:&lt;pre&gt;&lt;code class=bash&gt;Monitoring boot status for iPhone-17-Pro-Max-Unit-Tests (BC8ABA4E-2FCD-4D79-AEE3-5F9B728496B4).&#xA;[2026-04-28 17:48:44 +0000] Status=1, isTerminal=NO, Elapsed=00:01.&#xA;&#x9;Waiting on BackBoard&#xA;&#xA;[2026-04-28 17:48:50 +0000] Status=4, isTerminal=NO, Elapsed=00:07.&#xA;&#x9;Waiting on System App&#xA;&#xA;[2026-04-28 17:48:52 +0000] Status=4294967295, isTerminal=YES, Elapsed=00:09.&#xA;&#x9;Finished&#xA;&#xA;( xcrun simctl boot ${SIM_ID} &amp;amp;&amp;amp; xcrun simctl bootstatus ${SIM_ID}; )  0.07s user 0.08s system 1% cpu 9.855 total&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;У нас в пайплайне идет 3 теста и 3 раза мы создаем симулятор. Если отталкиваться от тестов выше, то переиспользование симуляторов дает нам +20 секунд дополнительного времени, а для трех целая минута. Пока это теория, поэтому обновляем тестовые &lt;code&gt;lane&lt;/code&gt; чтобы проверить. Для простоты эксперимента просто захардкодим ID симуляторов:&lt;pre&gt;&lt;code class=bash&gt;lane :run_unit_tests do&#xA;    run_testing(&#xA;        scheme: &amp;#34;AppTests&amp;#34;,&#xA;        testplan: &amp;#34;AppTests&amp;#34;,&#xA;        destination: &amp;#34;8A23906D-84CA-42FA-9703-1CA8986B11D9&amp;#34;&#xA;    )&#xA;end&#xA;&#xA;lane :run_ui_tests do&#xA;    run_testing(&#xA;        scheme: &amp;#34;AppUITests&amp;#34;,&#xA;        testplan: &amp;#34;AppUITests&amp;#34;,&#xA;        destination: &amp;#34;8A23906D-84CA-42FA-9703-1CA8986B11D9&amp;#34;&#xA;    )&#xA;end&#xA;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;А из &lt;code&gt;lane&lt;/code&gt; пускателя тестов убираем создание и удаление симулятора&lt;pre&gt;&lt;code class=ruby&gt;private_lane :run_testing do |options|&#xA;    uuid = options[:destination]&#xA;    sh &amp;#34;xcrun simctl boot #{uuid}&amp;#34;&#xA;    sh &amp;#34;xcrun simctl bootstatus #{uuid}&amp;#34;&#xA;    begin&#xA;        scan(&#xA;            destination: &amp;#34;platform=iOS Simulator,id=#{uuid}&amp;#34;,&#xA;            ...&#xA;        )&#xA;    ensure&#xA;        sh &amp;#34;xcrun simctl shutdown #{uuid}&amp;#34;&#xA;    end&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Пушим в репо и перезапускам весь пайплайн и средний результат у нас получается 7 минут и 10 секунд, что на 1 минуту и 30 секунд быстрее предыдущего результата (теоретические расчеты примерно эту цифру и показывали).&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/1b/94/3e/1b943ee435f7105974db4a7c72478cd7.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/1b/94/3e/1b943ee435f7105974db4a7c72478cd7.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/1b/94/3e/1b943ee435f7105974db4a7c72478cd7.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;На данный момент мы ускорили пайплайн на 35.24%. Идем дальше.&lt;hr&gt;&lt;h2&gt;Выжимаем максимум из симуляторов&lt;/h2&gt;&lt;p&gt;Следующий шагом предлагаю снова улучшить работу с симуляторами. На этот раз не будем закрывать (shutdown) симулятор после теста. Более того, мы на проекте сделали следующий хинт, по &lt;code&gt;cron&lt;/code&gt; у нас перед началом работы каждое утро рабочего дня запускается задача, которая:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Закрывает все симуляторы&lt;/ol&gt;&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl shutdown all&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol start=2&gt;&lt;li&gt;&lt;p&gt;Чистит все симуляторы&lt;/ol&gt;&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl erase all&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol start=3&gt;&lt;li&gt;&lt;p&gt;Создает новые симуляторы если необходимо&lt;li&gt;&lt;p&gt;Запускает необходимые симуляторы, чтобы, когда запускались тесты, они уже были “прогреты”&lt;/ol&gt;&lt;p&gt;Правим наш &lt;code&gt;run_testing&lt;/code&gt; чтобы он:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Если симулятор не запущен, то запускал перед тестом&lt;li&gt;&lt;p&gt;Не закрывал симулятор после теста, чтобы он был переиспользуемым&lt;/ol&gt;&lt;pre&gt;&lt;code class=ruby&gt;private_lane :run_testing do |options|&#xA;    uuid = options[:destination]&#xA;    is_booted = (sh &amp;#34;xcrun simctl list devices booted&amp;#34;).include?(uuid)&#xA;    unless is_booted&#xA;        sh &amp;#34;xcrun simctl boot #{uuid}&amp;#34;&#xA;        sh &amp;#34;xcrun simctl bootstatus #{uuid}&amp;#34;&#xA;    end&#xA;    scan(&#xA;        destination: &amp;#34;platform=iOS Simulator,id=#{uuid}&amp;#34;,&#xA;        ...&#xA;    )&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Пушим изменения и прогоняем пайплайн очередной раз. Получаем картину:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/fe/d8/83/fed883bd575a29250f73ae026d456df1.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/fe/d8/83/fed883bd575a29250f73ae026d456df1.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/fe/d8/83/fed883bd575a29250f73ae026d456df1.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Где средний результат 6 минут и 8 секунд, что на 1 минуту лучше предыдущего. На данный момент мы ускорили пайплайн на 44.58%. Идем дальше.&lt;hr&gt;&lt;h2&gt;Разбиваем джобы по раннерам&lt;/h2&gt;&lt;p&gt;Сделаем анализ джоб в пайплайне.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Сборка проекта (долгая и тяжелая для процессора работа)&lt;li&gt;&lt;p&gt;Юнит тесты (легкая работа, слабо нагружает процессор)&lt;li&gt;&lt;p&gt;UI тесты (тяжелые продолжительные тесты)&lt;/ol&gt;&lt;p&gt;Из анализа видно, что юнит тесты это легкая работа и если юнит тесты параллельно запускать с тяжелой джобой, то существенного эффекта на тяжелую задачу не будет. Если параллельно запускать тяжелые джобы, то они будут мешать другу, и, например, почти точно UI тесты начнут флаковать чаще обычного.&lt;p&gt;Наша цель, запускать юнит тесты вместе с UI тестами параллельно. Для этого создадим второй раннер на CI. У нас есть один дефолтный раннер &lt;code&gt;ios&lt;/code&gt;, который теперь будет отвечать за выполнение тяжелых задач. И новый раннер, на котором мы будем запускать только легкие задачи. Разрешим новому раннеру запускать до 2 легких задач параллельно, а старому раннеру разрешим только 1 тяжелую джобу, а самому CI выставить возможность запускать только 2 джобы в параллели. То есть у нас теперь могут запускаться такие комбинации:&lt;ol&gt;&lt;li&gt;&lt;p&gt;1 легкая задача + 1 тяжелая задача&lt;li&gt;&lt;p&gt;2 легких задачи&lt;li&gt;&lt;p&gt;Если нет легких задач, то запуститься только одна тяжелая задача&lt;/ol&gt;&lt;p&gt;Создаем второй раннер с тегом &lt;code&gt;ios_low_load&lt;/code&gt; и прописываем в ~/.gitlab-runner/config.toml (если вы используете GitLab):&lt;pre&gt;&lt;code&gt;concurrent = 2&#xA;&#xA;[[runners]]&#xA;  name = &amp;#34;ios&amp;#34;&#xA;  limit = 1&#xA;  &#xA;[[runners]]&#xA;  name = &amp;#34;ios_low_load&amp;#34;&#xA;  limit = 2&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Затем раставляем эти теги в &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, тут всё просто, только для юнит тестов выставляем новый раннер &lt;code&gt;ios_low_load&lt;/code&gt;, для остальных оставляем старый.&lt;pre&gt;&lt;code&gt;iOS iPhone | Unit Tests:&#xA;  tags:&#xA;    - ios_low_load&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Пушим и запускаем. Так как у нас юнит тесты занимают почти 30-40 секунд, в теории мы должны получить прирост примерно такой же.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/ec/99/96/ec9996b0b7538d91e4c137b719e524b9.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/ec/99/96/ec9996b0b7538d91e4c137b719e524b9.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/ec/99/96/ec9996b0b7538d91e4c137b719e524b9.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Если сложить время всех задач, то получим 5 минут 49 секунд. Но время самого пайплайна 5 минут и 23 секунды, потому что часть задач стали выполняться параллельно. Что дало нам прирост в скорости, чем дольше бы у нас были юнит тесты, тем больше бы прирост мы получили. На данном этапе мы улучшили скорость пайплайна на 51.36%, то есть в 2 раза, как вы помните, без всех изменений выше время пайплайна было больше 11 минут. Переходим на финальный этап.&lt;hr&gt;&lt;h2&gt;Распараллеливаем UI тесты&lt;/h2&gt;&lt;p&gt;Наверное, самая холиварная часть этой статьи. Сначала определимся, что значит параллельное UI тестирование. Xcode (&lt;code&gt;xcodebuild&lt;/code&gt; если точнее) имеет механизм параллельного тестирования на нескольких симуляторах в рамках одного тестового запуска. Вот так выглядит симулятор, когда параллельное тестирование выключено:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/29/28/52/292852b52e30b6ed839aab442f7c94d7.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/29/28/52/292852b52e30b6ed839aab442f7c94d7.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/29/28/52/292852b52e30b6ed839aab442f7c94d7.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;А вывод команды запущенных симуляторов выглядит так:&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl list devices booted&#xA;== Devices ==&#xA;-- iOS 26.4 --&#xA;    iPhone-17-Pro-Max-Unit-Tests (8A23906D-84CA-42FA-9703-1CA8986B11D9) (Booted) &#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;А так выглядят симуляторы при параллельном тестировании:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/7c/3e/0f/7c3e0fcc30761eecb19712ed59925e39.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/7c/3e/0f/7c3e0fcc30761eecb19712ed59925e39.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/7c/3e/0f/7c3e0fcc30761eecb19712ed59925e39.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Если во время теста мы запустим команду получения списка запущенных симуляторов:&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl list devices booted&#xA;== Devices ==&#xA;-- iOS 26.4 --&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;То получим пустой список. Из скриншота видно, что это не обычные симуляторы, а клоны симулятора, который был передан через &lt;code&gt;-destination&lt;/code&gt;. Так как эти симуялторы не трекаются &lt;code&gt;simctl&lt;/code&gt;, то во время теста мы не можем ими управлять, например если захотим во время теста средствами &lt;code&gt;simctl&lt;/code&gt; получиться скриншот экрана, мы этого не сможем сделать, потому что нам не известен UDID симулятора.&lt;p&gt;Внимательный читатель, возможно, заметил, что в &lt;code&gt;fastlane&lt;/code&gt; командах выше мы использовали &lt;code&gt;disable_concurrent_testing: true&lt;/code&gt; которые для &lt;code&gt;xcodebuild&lt;/code&gt; транслируется в &lt;code&gt;-disable-concurrent-testing&lt;/code&gt; чтобы отключить параллельное тестирование, но на самом деле этот флаг ничего не делает так как мы запускаем тесты через &lt;code&gt;testPlan&lt;/code&gt;, а &lt;code&gt;testPlan&lt;/code&gt; имеет свою настройку для параллельного запуска и она берет приоритет над &lt;code&gt;-disable-concurrent-testing&lt;/code&gt;. Мы этот флаг в &lt;code&gt;fastlane&lt;/code&gt; использовали, чтобы подчеркнуть, что запускаем тест не в параллельном режим, хотя на самом деле это было выключено на уровне &lt;code&gt;testPlan&lt;/code&gt; вот тут:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/ba/4b/88/ba4b88447c71739d1817b795c8c3b1dc.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/ba/4b/88/ba4b88447c71739d1817b795c8c3b1dc.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/ba/4b/88/ba4b88447c71739d1817b795c8c3b1dc.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Теперь опишем как Xcode запускает тесты в параллельном режиме (включено в &lt;code&gt;testPlan&lt;/code&gt;). Когда вы запускаете команду:&lt;pre&gt;&lt;code class=bash&gt;xcodebuild \&#xA;-destination &amp;#39;platform=iOS Simulator,id=8A23906D-84CA-42FA-9703-1CA8986B11D9&amp;#39; \&#xA;-testPlan &amp;#39;UITests-iPhone&amp;#39; \&#xA;-testProductsPath AppUITests.xctestproducts \&#xA;test-without-building&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;xcodebuild&lt;/code&gt; завершает работу симулятора (8A23906D-84CA-42FA-9703-1CA8986B11D9) если он запущен&lt;li&gt;&lt;p&gt;Создает клонов этого симулятора сколько считает нужным если явно не задано через команду (&lt;code&gt;-parallel-testing-worker-count N&lt;/code&gt;) (где N - количество клонов):&lt;/ol&gt;&lt;pre&gt;&lt;code class=bash&gt;xcrun simctl clone 8A23906D-84CA-42FA-9703-1CA8986B11D9 &amp;#34;Clone ${N} ${DEVICE_NAME}&amp;#34;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;ol start=3&gt;&lt;li&gt;&lt;p&gt;Запускает параллельно тесты на клонах&lt;li&gt;&lt;p&gt;После завершения тестов клоны уничтожаются (их нельзя переиспользовать, потому что у них нет UDID)&lt;/ol&gt;&lt;p&gt;На базе этого мы делаем выводы, где у такого подхода могут быть проблемы:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Клоны не имеют UDID и мы не можем ими управлять&lt;li&gt;&lt;p&gt;Клоны всегда перезапускаются, невозможно создать pool клонов и переиспользовать, чтобы не тратить время на запуск, так как запуск симуляторов это тяжелая операция для CPU (откройте &lt;code&gt;htop&lt;/code&gt; и сделайте &lt;code&gt;boot&lt;/code&gt; симулятору). Вот скриншот с мощной &lt;code&gt;M&lt;/code&gt; машины, тут &lt;code&gt;xcodebuild&lt;/code&gt; запускает одного клона для тестов:&lt;/ol&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/fc/cd/79/fccd7985f78e1c564c804b12f17773c9.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/fc/cd/79/fccd7985f78e1c564c804b12f17773c9.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/fc/cd/79/fccd7985f78e1c564c804b12f17773c9.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;А ведь для параллельного тестирования мы будем создавать 4-5-6 клонов (среднее устоявшееся кол-во при котором тесты на флакуют), и запуск этих 4-6 клонов с легкостью съедают от 3 до 5 минут прежде, чем все тесты в параллели запустятся на симуляторах. Если у вас немного тестов, то ситуация бывает такой, что на первом клоне тесты уже прошли, а четвертый или пятый еще даже не прогрузились.&lt;p&gt;Но опять же это всё теория. Давайте делать замеры.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Тесты без параллельного тестирования для нашего тест плана мы можем взять из джоб, что мы описывали выше, последний результат был 2 минуты и 10 секунд&lt;li&gt;&lt;p&gt;Тесты с параллельным режимом. Наш тест план имеет всего 2 теста (2 тестовых XCTest класса), но все равно проверим. В &lt;code&gt;fastlane&lt;/code&gt; выставляем &lt;code&gt;concurrent_workers: 2&lt;/code&gt; чтобы точно запустилось нужное нам кол-во клонов-симуляторов. Два перезапуска и вот результат:&lt;/ol&gt;&lt;pre&gt;&lt;code class=ruby&gt;private_lane :run_testing do |options|&#xA;  scan(&#xA;    concurrent_workers: 2,&#xA;    ...&#xA;  )&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=bash&gt;xcodebuild \&#xA;    -destination &amp;#39;platform=iOS Simulator,id=8A23906D-84CA-42FA-9703-1CA8986B11D9&amp;#39; \&#xA;    -derivedDataPath AppUITests.xctestproducts \&#xA;    -parallel-testing-worker-count 2 \&#xA;    -testPlan &amp;#39;AppUITests&amp;#39; \&#xA;    -testProductsPath AppUITests.xctestproducts \&#xA;    test-without-building &#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/a1/73/bf/a173bf843f697693da0377b997ef7083.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/a1/73/bf/a173bf843f697693da0377b997ef7083.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/a1/73/bf/a173bf843f697693da0377b997ef7083.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;2 минуты и 30 секунд, что даже медленнее, чем при последовательном запуске на одном симуляторе. Этому есть простое объяснение - время перезапуска клонов очень долгое, что нивелирует весь профит от параллельного запуска (но тут важно сказать, что это только для малого кол-ва тестов, в больших масштабах время запуска практически погрешность и там уже другие метрики)&lt;ol start=3&gt;&lt;li&gt;&lt;p&gt;Делаем замер параллельного запуска, но ручного:&lt;/ol&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Выключаем в тест плане параллельный запуск&lt;li&gt;&lt;p&gt;На каждый тест назначаем уже прогретый симулятор сами&lt;/ul&gt;&lt;p&gt;Так как мы выключили автоматический параллельный запуск, нам нужно самим написать код, который раскидывает тесты по симуляторам. В теории рассортировать тесты по симуляторам несложно, например сходу у меня было решение:&lt;pre&gt;&lt;code class=ruby&gt;lane :run_ui_tests do |options|&#xA;    thread1 = Thread.new do&#xA;        scan(&#xA;            destination: &amp;#34;platform=iOS Simulator,id=#{SIMULATOR_1}&amp;#34;,&#xA;            only_testing: [&#xA;                &amp;#34;UITest-1&amp;#34;,&#xA;                &amp;#34;UITest-2&amp;#34;,&#xA;            ]&#xA;        )&#xA;    end&#xA;&#xA;    thread2 = Thread.new do&#xA;        scan(&#xA;            destination: &amp;#34;platform=iOS Simulator,id=#{SIMULATOR_2}&amp;#34;,&#xA;            only_testing: [&#xA;                &amp;#34;UITest-3&amp;#34;,&#xA;                &amp;#34;UITest-4&amp;#34;,&#xA;            ]&#xA;        )&#xA;    end&#xA;&#xA;    thread1.join&#xA;    thread2.join&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;К сожалению, &lt;code&gt;fastlane&lt;/code&gt; не поддерживает многопоточность. Поэтому это пришлось параллелизацию вынести из &lt;code&gt;fastlane&lt;/code&gt; на &lt;code&gt;bash&lt;/code&gt;:&lt;pre&gt;&lt;code class=ruby&gt;lane :run_ui_tests do |options|&#xA;    scan(&#xA;        destination: &amp;#34;platform=iOS Simulator,id=#{options[:destination]}&amp;#34;,&#xA;        only_testing: options[:only_testing].split(&amp;#34;,&amp;#34;),&#xA;        disable_concurrent_testing: true&#xA;    )&#xA;end&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=bash&gt;fastlane ios run_ui_tests destination:${SIM_1} only_testing:&amp;#39;UITest-1,UITest-2&amp;#39; &amp;amp; \&#xA;    fastlane ios run_ui_tests destination:${SIM_2} only_testing:&amp;#39;UITest-3,UITest-4&amp;#39; &amp;amp; \&#xA;    wait&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Очевидно получаем прирост скорости, потому что параллелизация и прогретые симуляторы, итоговое время 1 минута и 38 секунд, что на 24.62% быстрее непараллельного режима и на 34.66% быстрее параллельного режима с холодным стартом клонов.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/c7/59/fc/c759fcdccda485f4f9b9717829d836fd.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/c7/59/fc/c759fcdccda485f4f9b9717829d836fd.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/c7/59/fc/c759fcdccda485f4f9b9717829d836fd.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Из чего мы делаем вывод, если у вас есть время и вы хотите выжать максимум из симуляторов для UI тестов, то вам нужно присмотреться к ручному управлению параллелизации на уже прогретых симуляторах. Ускорение пайплайна на 3-5 минут кажется не существенным, но, если пайплайн запускается десятки раз за день, это время уже идет на часы.&lt;p&gt;Если вы только настраиваете запуск тестов на CI, я рекомендую посмотреть на &lt;a href=https://github.com/MarathonLabs/marathon rel=&#34;noopener noreferrer nofollow&#34;&gt;Maraphon&lt;/a&gt;. По их документации, они как раз решают проблему с холодным стартом клонов, просто вместо клонов создавая и кешируя нормальные симуляторы.&lt;p&gt;Основано на реальном проекте:&lt;p&gt;Была в одном проекте как раз такая проблемная джоба, которая запускала UI тесты. Время ее прогона было около 30 минут (4 симулятора в параллели через клоны, больше CI не тянул). Тестов было и правда много - сотня-две. Джоба запускалась довольно часто (до 10 раз в день легко, а если релиз и того больше). Постоянно в чате было, что QA ждёт, когда пройдет пайплайн, чтобы забрать билд на проверку, да и сами разработчики ждали, потому что UI тесты грузили сильно CI, второй пайплайн не запустишь.&lt;p&gt;В итоге после применения всех пунктов, что описаны выше в статье, удалось снизить время выполнения UI тестов с 30 минут до 12-15 минут насколько помню. После данные оптимизации были добавлены и в другие UI тесты, и даже юнит тесты нам удалось ускорить в несколько раз, потом что мы написали тулу, которая на лету разбивала тест план на несколько тест планов и запускала их на свободных прогретых симуляторах. Я потом делал расчет времени каждого теста и их суммарное время на CI. И у меня выходило, что тесты выполнялись за теоретический максимум, так как в тестах не было потерь времени на что-то другое кроме тестов (запуск симулятор и прочее). То есть пришли в итоге к такому состоянию, что просто уже нечего было улучшать. Теоретический предел посчитать легко это сумма времени каждого теста, деленное на кол-во симуляторов, время тестов можно вытащить из &lt;code&gt;.xcresult&lt;/code&gt;. Если у вас это время сильно отличается от времени прохождения джобы/пайплайна, то нужно искать, где это время теряется, и попытаться убрать этот bottle neck.&lt;hr&gt;&lt;h2&gt;Бонус, кто дочитал до конца, что такое .xctestproducts&lt;/h2&gt;&lt;p&gt;Это директория очень напоминает DerivedData проекта, но структурирована немного иначе. Директрия содержит не только результат сборки приложения, но и, логично, скомпилированне тест планы, что есть &lt;code&gt;${TESTPLAN_NAME}.xctestrun&lt;/code&gt;, что из себя представляет обычный &lt;code&gt;plist&lt;/code&gt; файл. Причем самое интересное, что этот файл мы можем изменять, если нужно изменить поведение теста, и не нужно после изменений снова перекомпилировать &lt;code&gt;testplan&lt;/code&gt;, можно сразу же использовать измененный &lt;code&gt;.xctestrun&lt;/code&gt;. Покажу пару примеров, которые я сам использовал:&lt;p&gt;В &lt;code&gt;.xctestrun&lt;/code&gt; есть такая секция:&lt;pre&gt;&lt;code class=xml&gt;&amp;lt;key&amp;gt;CommandLineArguments&amp;lt;/key&amp;gt;&#xA;&amp;lt;array&amp;gt;&#xA;    &amp;lt;string&amp;gt;-AppleLanguages&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;(en)&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;-AppleTextDirection&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;NO&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;-AppleLocale&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;en_US&amp;lt;/string&amp;gt;&#xA;&amp;lt;/array&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Если мы хотим запустить тесты (например снапшот тесты) для разных языков, мы можем один раз собрать приложение, и меняя &lt;code&gt;.xctestrun&lt;/code&gt; без пересборки приложения прогонять тесты на разных языках очень быстро. Например, для французского языка выставляем:&lt;pre&gt;&lt;code class=xml&gt;&amp;lt;key&amp;gt;CommandLineArguments&amp;lt;/key&amp;gt;&#xA;&amp;lt;array&amp;gt;&#xA;    &amp;lt;string&amp;gt;-AppleLanguages&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;(fr)&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;-AppleTextDirection&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;NO&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;-AppleLocale&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;fr_FR&amp;lt;/string&amp;gt;&#xA;&amp;lt;/array&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Следующая интересная секция:&lt;pre&gt;&lt;code class=xml&gt;&amp;lt;key&amp;gt;OnlyTestIdentifiers&amp;lt;/key&amp;gt;&#xA;&amp;lt;array&amp;gt;&#xA;    &amp;lt;string&amp;gt;UITest1/test()&amp;lt;/string&amp;gt;&#xA;    &amp;lt;string&amp;gt;UITest2/test()&amp;lt;/string&amp;gt;&#xA;&amp;lt;/array&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Это список тестов, которые будут запущены. Мы как раз писали мини тулу, которая берет отсюда список тестов, группирует их по времени выполнения (чтобы каждая группа тестов выполнялась за одно время), создает новый тест план под каждый симулятор и запускает в параллельном режиме.&lt;p&gt;И последняя довольно интересная секция:&lt;pre&gt;&lt;code class=xml&gt;&amp;lt;key&amp;gt;EnvironmentVariables&amp;lt;/key&amp;gt;&#xA;&amp;lt;dict&amp;gt;&#xA;    &amp;lt;key&amp;gt;LOCAL_PORT&amp;lt;/key&amp;gt;&#xA;    &amp;lt;string&amp;gt;8000&amp;lt;/string&amp;gt;&#xA;&amp;lt;/dict&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Через &lt;code&gt;EnvironmentVariables&lt;/code&gt; можно пробрасывать разлиные данные в тестовое приложение, например чтобы не хардкодить &lt;code&gt;port&lt;/code&gt; для локального сервера, который предоставляет приложению тестовые данные, его можно передать как через &lt;code&gt;testplan&lt;/code&gt;, так и напрямую через &lt;code&gt;.xctestrun&lt;/code&gt;. Например, если у вас несколько CI и вы передали на другой CI &lt;code&gt;.xctestproducts&lt;/code&gt;, то сборщик, возможно, не имеет знаний, какой &lt;code&gt;port&lt;/code&gt; нужно было выставить в оригинальном &lt;code&gt;testplan&lt;/code&gt;, поэтому уже на месте исполнения тестов это можно пробросить именно тут.&lt;hr&gt;&lt;p&gt;Цель статьи:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Не растерять накопленные знания&lt;li&gt;&lt;p&gt;Скомпоновать знания в читаемый вид (до этого это лежало кусками в разных местах)&lt;li&gt;&lt;p&gt;Поделиться с сообществом&lt;li&gt;&lt;p&gt;Себе шпаргалка для будущих проектов&lt;/ol&gt;&lt;p&gt;Спасибо всем, кто дочитал. Надеюсь было полезно.&lt;p&gt;Для написания статьи ИИ не использовался, только спелчеккер для русского языка в IntelliJ Idea.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>house2008</author>
      <guid>https://habr.com/ru/articles/1030150/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030150</guid>
      <pubDate>Thu, 30 Apr 2026 13:00:09 +0000</pubDate>
    </item>
    <item>
      <title>Вредные советы для Flutter-разработчика</title>
      <link>https://habr.com/ru/companies/agima/articles/1030072/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030072</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;&lt;em&gt;Назидательная статья, написанная в стихах и вдохновленная детскими книгами. Посвящается всем ошибкам, которые я совершал на работе — большим и маленьким.&lt;/em&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/b4e/751/c71/b4e751c719351dac8576fba0a498ed7a.png width=1536 height=1024 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/b4e/751/c71/b4e751c719351dac8576fba0a498ed7a.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/b4e/751/c71/b4e751c719351dac8576fba0a498ed7a.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Привет! Вообще-то я руковожу мобильной и фронтенд-разработкой в &lt;a href=&#34;https://www.agima.ru/?utm_source=habr&amp;amp;utm_medium=article&amp;amp;utm_campaign=poem_flutter&#34;&gt;AGIMA&lt;/a&gt;, но сегодня я тут в другом амплуа. Помогаю всем желающим запомнить, как писать код на Flutter &lt;strong&gt;&lt;em&gt;не надо&lt;/em&gt;&lt;/strong&gt;. Чтобы запомнить вредные советы было проще — воспользуемся стихотворной формой.&lt;h2&gt;Вступление&lt;/h2&gt;&lt;p&gt;1. &lt;p&gt;На любое время года&lt;br&gt;Есть отличная метода&lt;br&gt;Написанья говнокода.&lt;br&gt;Никакой тут тайны нету —&lt;br&gt;Каждому помогут в этом&lt;br&gt;Наши вредные советы.&lt;p&gt;2.&lt;p&gt;Наливай чай-кофе в чашку —&lt;br&gt;Или что в твоей баклажке? —&lt;br&gt;И знакомься с объясняшкой.&lt;br&gt;Для начала — сразу твист:&lt;br&gt;Дельную найдешь ты мысль,&lt;br&gt;&lt;em&gt;Только если флаттерист&lt;/em&gt;.&lt;p&gt;3.&lt;p&gt;Применять советы нужно&lt;br&gt;Перорально и наружно,&lt;br&gt;Когда весело и скучно.&lt;br&gt;Следуй им беспрекословно —&lt;br&gt;Польза будет баснословной,&lt;br&gt;Что для Девы, что для Овна.&lt;p&gt;4.&lt;p&gt;Прочитай их перед сном&lt;br&gt;Вместо всяких .ru и .com —&lt;br&gt;Будешь писать кипятком.&lt;br&gt;Мал или велик твой стаж,&lt;br&gt;Это высший пилотаж.&lt;br&gt;Вот и кончен инструктаж.&lt;h2&gt;Основная часть&lt;/h2&gt;&lt;p&gt;5.&lt;p&gt;День рабочий — это счастье:&lt;br&gt;Чтобы в мелочность не впасть нам,&lt;br&gt;&lt;em&gt;Не дели проект на части&lt;/em&gt;.&lt;br&gt;В main фиксируй мысли ток,&lt;br&gt;Чтоб никто прочесть не мог&lt;br&gt;Твой десяток тысяч строк.&lt;p&gt;6.&lt;p&gt;Дальше тема посложнее.&lt;br&gt;Если нужно в приложеньи&lt;br&gt;Состояньем управленье,&lt;br&gt;Всюду пользуйся setState —&lt;br&gt;Никаких не будет бед!&lt;br&gt;Это норм такой совет.&lt;p&gt;7.&lt;p&gt;Сделав очень важный вид,&lt;br&gt;Нажимай всегда Rebuild —&lt;br&gt;И работа закипит!&lt;br&gt;Даже если changed один,&lt;br&gt;Обновляй весь магазин.&lt;br&gt;Это чистый дофамин.&lt;p&gt;8.&lt;p&gt;Вот подумай сам немного:&lt;br&gt;Ну зачем тебе, ей-богу,&lt;br&gt;Легкую искать дорогу?&lt;br&gt;Хочешь быть обут-одет,&lt;br&gt;Оснований просто нет&lt;br&gt;Не использовать setState.&lt;br&gt;&lt;br&gt;9.&lt;p&gt;Если, скажем, строя дом,&lt;br&gt;Сделал лишний ты проем,&lt;br&gt;То какой совет даем?&lt;br&gt;Верно, сносим дом под корень —&lt;br&gt;Чтобы был с землею вровень.&lt;br&gt;Важно, чтоб ты принцип понял!&lt;p&gt;10.&lt;p&gt;Есть еще завет для тех,&lt;br&gt;Кто настроен на успех:&lt;br&gt;Асинхронность — страшный грех.&lt;br&gt;Лучше брать за каткой катку,&lt;br&gt;Потихоньку, по порядку.&lt;br&gt;Вот он — путь для самых хватких.&lt;p&gt;11.&lt;p&gt;Понял жизнь? Так не спеши.&lt;br&gt;Код ты пишешь для души —&lt;br&gt;Пусть растет и вглубь, и вширь.&lt;br&gt;А async/await — измена,&lt;br&gt;Не понять им чувства дзена.&lt;br&gt;&lt;em&gt;Где б еще добавить then’а?&lt;/em&gt;&lt;p&gt;12.&lt;p&gt;Дальше — больше: DTO.&lt;br&gt;Надо с ходу брать его,&lt;br&gt;Словно норму ГТО.&lt;br&gt;Ну кому нужны модели?&lt;br&gt;Мы и так тут еле-еле&lt;br&gt;До обеда досидели.&lt;p&gt;13. &lt;p&gt;Нет, серьезно. Просто знай:&lt;br&gt;API передать в UI —&lt;br&gt;Это самый чистый кайф.&lt;br&gt;Усомнишься: «Вдруг контракт&lt;br&gt;Завтра сменят?» Если так,&lt;br&gt;Перепишешь всё. Пустяк.&lt;p&gt;14.&lt;p&gt;Pagination — это роскошь.&lt;br&gt;У кого сейчас ни спросишь,&lt;br&gt;Всякий скажет: это просто ж —&lt;br&gt;Надо загрузить всё сразу:&lt;br&gt;Виджеты, картинки, базы —&lt;br&gt;Это классно, это маза.&lt;p&gt;15.&lt;p&gt;Если данных миллиард —&lt;br&gt;Пользователь будет рад&lt;br&gt;Подождать три дня подряд.&lt;br&gt;С интернетом ныне плохо —&lt;br&gt;Время есть на охи-вздохи.&lt;br&gt;Не загрузится — и ладно.&lt;p&gt;16.&lt;p&gt;Плагины с онлайн-просторов&lt;br&gt;Выбирай без лишних споров.&lt;br&gt;Здесь не место для разборов.&lt;br&gt;Если в сборке есть конфликт,&lt;br&gt;Значит, сборка полетит,&lt;br&gt;Словно гоночный болид.&lt;p&gt;17.&lt;p&gt;Значит, драма в сборке есть,&lt;br&gt;Есть сомненье, есть протест.&lt;br&gt;Возбуждает интерес!&lt;br&gt;Плагина-героя злой&lt;br&gt;Плагин выманил на бой.&lt;br&gt;Чувствуешь, накал какой?&lt;p&gt;18.&lt;p&gt;Здесь добавим мы для веса:&lt;br&gt;Должен код быть &lt;strong&gt;&lt;em&gt;интересен&lt;/em&gt;&lt;/strong&gt;,&lt;br&gt;Как сюжет старинных песен.&lt;br&gt;Если прост он и понятен,&lt;br&gt;Нет ошибок в нем и пятен, —&lt;br&gt;Без работы мы, приятель!&lt;p&gt;19.&lt;p&gt;Дальше скажем для проформы,&lt;br&gt;Что учитывать платформы —&lt;br&gt;В наши дни уже не норма.&lt;br&gt;Что Android, что iOS —&lt;br&gt;Лучше делать на авось.&lt;br&gt;Наплевать, какая ось.&lt;p&gt;20.&lt;p&gt;Миллиметр влево-вправо…&lt;br&gt;Если кнопка мимо встала —&lt;br&gt;Признак профессионала.&lt;br&gt;Честно, тут у нас не тир:&lt;br&gt;Не попал — не рухнул мир.&lt;br&gt;Ты не врач и не факир.&lt;p&gt;21.&lt;p&gt;Есть у нас одно словцо,&lt;br&gt;Шесть слогов в нем. Про-из-во-&lt;br&gt;ди-тель-ность… Ни то ни сё!&lt;br&gt;Нам такие бы слова&lt;br&gt;Вычеркнуть из словаря.&lt;br&gt;Там лежат они зазря.&lt;p&gt;22.&lt;p&gt;Вот еще рабочий слоган:&lt;br&gt;&lt;em&gt;«FPS — ненужный орган»&lt;/em&gt;.&lt;br&gt;Весь наш мир подчас издёрган.&lt;br&gt;Плавность — миф. Пускай летят&lt;br&gt;Кадры вбок, вперед, назад&lt;br&gt;Шустро, будто клин утят.&lt;p&gt;23.&lt;p&gt;Анимаций нету плавных —&lt;br&gt;Быть не может ровных планов&lt;br&gt;Даже у больших экранов.&lt;br&gt;Если дергается что-то —&lt;br&gt;Это не твоя забота.&lt;br&gt;Так устроена природа!&lt;p&gt;24.&lt;p&gt;Что еще? Забей на доки,&lt;br&gt;Если очень давят сроки.&lt;br&gt;И без них всё будет оки.&lt;br&gt;Для чего дана нам память?&lt;br&gt;Чтобы лишний раз не спамить.&lt;br&gt;Лучше голый код оставить.&lt;p&gt;25.&lt;p&gt;Что бы там ни думал босс,&lt;br&gt;Доки вызывают птоз,&lt;br&gt;Токсикоз, понос, склероз,&lt;br&gt;Аллергию, геморрой, &lt;br&gt;Недержание порой.&lt;br&gt;Обходи их стороной.&lt;p&gt;26.&lt;p&gt;Если через день релиз —&lt;br&gt;Сильно ты не торопись.&lt;br&gt;Будет пусть для всех сюрприз.&lt;br&gt;Если до релиза час —&lt;br&gt;Вот тогда пора и в пляс.&lt;br&gt;Куча времени у нас.&lt;p&gt;27.&lt;p&gt;Следуй правилу всегда ты:&lt;br&gt;Слышишь от начальства маты —&lt;br&gt;Собирай сертификаты.&lt;br&gt;Если твой тимлид молчит&lt;br&gt;И спокойный держит вид,&lt;br&gt;Значит, дело не горит.&lt;p&gt;28.&lt;p&gt;Лучше баги не логируй —&lt;br&gt;Сам себя ударишь гирей.&lt;br&gt;Без того обильно в «Джире».&lt;br&gt;Хочешь верь, хочешь не верь,&lt;br&gt;Настоящий инженер,&lt;br&gt;Чуять должен, например.&lt;p&gt;29.&lt;p&gt;Да, еще момент один:&lt;br&gt;Лучший лог для бага — print.&lt;br&gt;Нечего драконить спринт.&lt;br&gt;Зафиксируй всё в debug:&lt;br&gt;Если не заявлен брак,&lt;br&gt;Значит, ты себе не враг.&lt;p&gt;30.&lt;p&gt;Сорри, что формулировки&lt;br&gt;Не всегда бывают ловки.&lt;br&gt;Дело близится к концовке.&lt;br&gt;Сборку делай сам сиди,&lt;br&gt;Позабудь CI/CD —&lt;br&gt;Всё умеешь ты, поди.&lt;p&gt;31.&lt;p&gt;Метрики — ненужный хлам,&lt;br&gt;А разметка — просто спам.&lt;br&gt;Рифму тут додумай сам.&lt;br&gt;И совет на ход ноги:&lt;br&gt;Нервы с детства береги —&lt;br&gt;Пользуйся всегда ИИ.&lt;h2&gt;Послесловие&lt;/h2&gt;&lt;p&gt;32.&lt;p&gt;Вот и всё — окончен опус.&lt;br&gt;Натянув сову на глобус,&lt;br&gt;Сотворили фокус-покус.&lt;br&gt;Выполняй советы эти —&lt;br&gt;Будешь при воде и свете,&lt;br&gt;При котлете и монете.&lt;p&gt;32.&lt;p&gt;Следовать им нелегко —&lt;br&gt;Пей за вредность молоко.&lt;br&gt;Прочитав, храни покой.&lt;br&gt;Есть еще одна побочка:&lt;br&gt;Опосля последней точки&lt;br&gt;Рифмовать захочешь точно.&lt;p&gt;33.&lt;p&gt;Опыт получился гарный&lt;br&gt;(Хоть, пожалуй, не для Хабра).&lt;br&gt;Не забудь досыпать кармы.&lt;br&gt;Где последняя строка?&lt;br&gt;На помин она легка.&lt;br&gt;Вот она. Бывай, пока.&lt;hr&gt;&lt;p&gt;В конце хочется напомнить, что сказка — ложь, да в ней намек. Как мы знаем, вредные советы нужны, чтобы им не следовать. Так что хорошо их изучите — и никогда так не делайте. Хороших проектов и чистого кода!&lt;h2&gt;Статьи про Flutter не в стихах&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/companies/agima/articles/892278/&gt;История одного Flutter-проекта, который заставил нас поломать голову&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/companies/agima/articles/873576/&gt;CodeStyle на Flutter-проектах: базовые принципы и правила — шаблон на все случаи жизни&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/companies/agima/articles/827732/&gt;Rive-анимация для Flutter-приложений: почему мы любим ее больше Lottie, когда ее применять и какие фишки использовать&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/agima/articles/1030072/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030072</guid>
      <pubDate>Thu, 30 Apr 2026 12:53:28 +0000</pubDate>
    </item>
    <item>
      <title>«М.Видео»: рынок бытовой техники и электроники давно стал узким для развития</title>
      <link>https://habr.com/ru/companies/mvideo/articles/1030140/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030140</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/027/e7f/723/027e7f723063cecb165bf1e4c54428a5.png alt=&#34;Владислав Бакальчук, гендиректор М.Видео&#34; title=&#34;Владислав Бакальчук, гендиректор М.Видео&#34; width=922 height=513 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/027/e7f/723/027e7f723063cecb165bf1e4c54428a5.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/027/e7f/723/027e7f723063cecb165bf1e4c54428a5.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Владислав Бакальчук, гендиректор М.Видео&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;em&gt;Как будет развиваться рынок непродовольственных товаров и где искать зоны роста? Об этом и многом другом журналистам РБК &lt;/em&gt;&lt;a href=https://www.rbc.ru/industries/news/69f306759a7947f7a6ff6c61&gt;&lt;em&gt;рассказал&lt;/em&gt;&lt;/a&gt;&lt;em&gt; гендиректор М.Видео Владислав Бакальчук.&lt;/em&gt;&lt;h4&gt;О рынке бытовой электроники&lt;/h4&gt;&lt;p&gt;Рынок бытовой техники и электроники не только не растет, но даже сокращается. По итогам 2025 года в натуральном выражении, по данным «ГФК-Русь», он вырос на 2,2%, до 403 млн единиц, а в денежном — сократился на 11,7%, до 2,63 трлн руб. Динамика объясняется тем, что, по подсчетам аналитиков, средняя цена устройства за год снизилась более чем на 14% — потребители отдают предпочтение более дешевым устройствам, тщательно сопоставляя цену и качество.&lt;p&gt;Также мы отмечаем и увеличение цикла обновления техники. Например, рынок смартфонов в 2025 году сократился на 19% и в штуках, и в деньгах, по оценкам тех же аналитиков. Средняя стоимость смартфона практически не изменилась, то есть рынок снижается не из-за цен, а из-за объемов — люди просто реже меняют устройства. При этом мы видим рост спроса на восстановленную технику. По итогам 2025 года продажи восстановленных смартфонов у нас выросли более чем вдвое в количественном выражении, а ноутбуков — на 70%. Это уже устойчивый тренд, и мы планируем активно развивать продажи таких товаров.&lt;p&gt;В целом рынок бытовой техники и электроники давно стал узким для развития «М.Видео»: жизненно важно не замыкаться внутри него, а выходить в другие сегменты. Поэтому мы изменили саму логику взаимодействия с клиентом — от разовой покупки смартфона или телевизора к регулярному контакту по самому широкому кругу товаров. Это влияет не только на структуру выручки, но и экономику бизнеса в целом: растет LTV (Lifetime Value — «время жизни клиента». — &lt;em&gt;«РБК Отрасли»&lt;/em&gt;), снижается стоимость удержания, укрепляется позиция платформы в повседневном потреблении. Мультикатегорийность в этой логике — инструмент увеличения частоты контакта и перехода в другой класс отношений с клиентом.&lt;p&gt;Опрос наших клиентов подтвердил запрос на расширение категорий: около 16% из них интересуют автотовары, по 15% — товары для дома, сада и мебель, 12% — товары для спорта и отдыха, 10% — посуда, 7% — ассортимент для строительства и ремонта.&lt;p&gt;Мы запустили десятки новых категорий и более чем вдвое увеличили число товарных позиций — с 200 тыс. в прошлом году до 450 тыс. сейчас, и уже видим результат. По итогам первого квартала 2026 года оборот нашего маркетплейса вырос более чем в три раза год к году, а по итогам апреля — уже до пяти раз год к году. Темпы продолжают ускоряться, подтверждая, что новая модель вошла в фазу масштабирования.&lt;h4&gt;Будущее за цифровыми платформами&lt;/h4&gt;&lt;p&gt;Во многих сегментах онлайн-продажи составляют уже более половины рынка: например, в секторе красоты и здоровья — 61%, детских товарах — 57%, одежде и обуви — 54%, по подсчетам Infoline. Но в сегментах, скажем, автотоваров, товаров для животных, спорта и отдыха, мебели или бытовой химии еще есть большой потенциал для развития.&lt;p&gt;Рынок бытовой техники и электроники уже функционирует по платформенной логике, где 55% продаж идут в онлайне и 67% из них через маркетплейсы.&lt;p&gt;В этих условиях приоритетом стала трансформация бизнеса в платформенную модель, где офлайн-инфраструктура выступает не центром, а одним из элементов системы продаж. Мы целимся достигнуть доли маркетплейса в онлайн-продажах компании на уровне 25% к концу 2026 года и 50% к 2028 году. Эти ориентиры основаны на переходе к гибкой 3P-модели (Third-Party — продажа товаров селлеров через платформу маркетплейса. — &lt;em&gt;«РБК Отрасли»&lt;/em&gt;), которая позволяет кратно наращивать ассортимент от сторонних продавцов при снижении нагрузки на оборотный капитал.&lt;p&gt;В большинстве категорий non-food (непродовольственных товаров) доля онлайн-продаж может дойти до 70–80% к 2035 году. Иначе говоря, этот рынок станет преимущественно онлайн-ориентированным, что формирует долгосрочный прогноз по наращиванию доли маркетплейсов на нем. И мы вышли на этот рынок, опираясь на уже готовую инфраструктуру: технологическую платформу, розничную сеть в сотнях городов, более 50 тыс. партнерских пунктов выдачи заказов и постаматов по всей стране и развитую логистику.&lt;h4&gt;Доверие и взаимовыгодное сотрудничество — основа бизнеса&lt;/h4&gt;&lt;p&gt;Наша аудитория — фактически все экономически активное население страны, это более 80 млн клиентов. Вопрос в том, как часто люди будут к нам возвращаться. Сегодня рынок вышел на уровень, где выигрывают не форматы, а бизнес-модели. Конкуренция идет не за ассортимент или цену, а за то, кто быстрее и точнее решает задачу клиента. Поэтому мы активно используем свою инфраструктуру, в которой розничная сеть, онлайн, широкий ассортимент, сервис и финтех дополняют друг друга и дифференцируют нас на рынке.&lt;p&gt;Отдельная задача — работа с восприятием платформы среди потенциальных партнеров-селлеров, которые должны увидеть, что теперь мы гораздо больше, чем продажа техники. Мы строим доверительные отношения с продавцами-партнерами — бесплатно храним их товары, вводим прозрачные комиссии, организуем доступную логистику и быстрое получение доходов от продаж, а также даем нестандартные сервисы, например возможность выкладки товаров в физической рознице. В I квартале этого года к нашей платформе присоединились 3200 новых селлеров, что в 1,6 раза больше, чем в IV квартале прошлого года.&lt;p&gt;Сегодня агентская схема и продажи товаров селлеров формируют уже около 35% оборота компании — до конца 2026 года планируем выйти на 45%, а к 2028-му — на 70%. Вслед за этим ростом кратно расширяется и ассортимент: до конца года платформа может достигнуть 1 млн товарных позиций, а к 2028 году — 10 млн.&lt;h4&gt;Заграница нам поможет&lt;/h4&gt;&lt;p&gt;Трансграничная торговля сегодня становится одним из эффективных инструментов расширения маркетплейса в тех категориях, где для покупателя критически важны три фактора: широкий выбор международных брендов, наличие глобальных версий устройств и конкурентная цена. Особенно ярко это проявляется в электронике, смартфонах, игровых консолях, аксессуарах и технике для дома.&lt;p&gt;По сути речь идет о переходе к глобальной товарной витрине. Мы уже начали продажи на маркетплейсе товаров из стран дальнего зарубежья, включая Китай, Гонконг и ОАЭ. На текущем этапе трансграничное предложение сосредоточено прежде всего в наиболее чувствительных к цене и ассортименту категориях. А в стадии подключения у компании уже находится пул международных продавцов с потенциальным ассортиментом более 20 млн позиций.&lt;h4&gt;О трансформации розничной сети&lt;/h4&gt;&lt;p&gt;Мы сосредотачиваем присутствие розничных магазинов в стратегически значимых локациях с максимальной операционной эффективностью. При этом в малых и удаленных городах переходим на партнерскую модель обслуживания. Интеграция со СДЭК и «Яндекс Доставкой» обеспечила доступ в более чем 4,5 тыс. населенных пунктов и тысячи пунктов выдачи без дополнительных инвестиций.&lt;p&gt;В целом магазины трансформируются из точек продаж в логистические узлы нашей платформы и сервисные хабы, которые оказывают разнообразные услуги. Например, здесь можно получить экспертную консультацию, протестировать новинку, отремонтировать технику, оформить трейд-ин.&lt;p&gt;Мы планируем развивать магазины двух форматов. Первый — пространство, куда покупатели придут за впечатлением, экспертизой, познакомиться с новинками, получить консультацию и совершить покупку. Второй — операционный, заточенный под скорость — выдача товара, возврат, логистика последней мили.&lt;p&gt;Часть магазинов будет превращаться в white store, где клиент может как сам оперативно забрать нужный товар с полки, так и мы — быстро оформить и отправить онлайн-заказ в соседний партнерский ПВЗ или на дом.&lt;h4&gt;О технологиях&lt;/h4&gt;&lt;p&gt;Идет полная замена IT-ландшафта компании с бюджетом около 9 млрд руб. В периметре трансформации будут новый сайт и мобильное приложение для покупателей, личный кабинет продавца, системы управления складами, транспортировкой и заказами, CRM, перевод инфраструктуры на Linux, а также встраивание ИИ в ценообразование, прогноз спроса и клиентский сервис. Это позволит в 2027 году перейти к полноценному масштабированию платформы. Внедрение технологий сильно ускоряет процессы: например, ИИ-модуль обработал уже свыше 240 тыс. обращений за первые месяцы этого года, снизив нагрузку на контактный центр по письменным ответам на 90%. Технологии в этой логике — не инструментарий, а несущая конструкция и двигатель бизнеса.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/mvideo/articles/1030140/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030140</guid>
      <pubDate>Thu, 30 Apr 2026 12:49:19 +0000</pubDate>
    </item>
    <item>
      <title>Установка Zabbix Agent 2 на Cloud Director Appliance (Photon OS 4)</title>
      <link>https://habr.com/ru/companies/cloud4y/articles/1030138/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030138</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;На Photon OS 4 (Cloud Director Appliance ≥ 10.6, vCenter Server Appliance 8) ставим Zabbix Agent 2 (LTS 7.0, RPM под EL8) &lt;strong&gt;без установки чужих RPM в систему&lt;/strong&gt;. Распаковываем три RPM (агент, openssl-libs 1.1, pcre2), кладём бинарь и конфиги штатно, недостающие .so складываем в /etc/zabbix/libs/ и подключаем к сервису через LD_LIBRARY_PATH в systemd-юните. На vCSA дополнительно извлекаем .hmac-файлы для FIPS. В финале — открытие порта 10050 и проверка с Zabbix-сервера через zabbix_get.&lt;p&gt;Шаги:&lt;p&gt;1.    Скачать RPM (агент + openssl-libs 1.1 + pcre2) на машине с интернетом.&lt;p&gt;2.    Распаковать, разложить файлы по нужным путям на appliance.&lt;p&gt;3.    Прописать LD_LIBRARY_PATH в unit-файле, запустить службу.&lt;p&gt;4.    Открыть порт 10050 и проверить доступность агента с сервера мониторинга.&lt;h3&gt;Зачем это нужно&lt;/h3&gt;&lt;p&gt;Photon OS — компактный базовый дистрибутив VMware для appliance-решений. Официально установка Zabbix Agent на Photon не поддерживается, а штатный пакетный менеджер tdnf не сможет закрыть зависимости агента: Photon OS 4 поставляется с OpenSSL 3 (&lt;a href=http://libssl.so&gt;libssl.so&lt;/a&gt;.3, &lt;a href=http://libcrypto.so&gt;libcrypto.so&lt;/a&gt;.3) и без PCRE2. Zabbix Agent 2, собранный для EL8, ждёт:&lt;p&gt;•       &lt;a href=http://libssl.so&gt;libssl.so&lt;/a&gt;.1.1&lt;p&gt;•       &lt;a href=http://libcrypto.so&gt;libcrypto.so&lt;/a&gt;.1.1&lt;p&gt;•       libpcre2-8.so.0&lt;p&gt;Доступ к репозиторию VMware с самого аплайнса часто закрыт сетевыми политиками, а ставить «чужие» RHEL-пакеты в систему рискованно — сломаются обновления самого аплайнса. Поэтому собираем «портативную» установку: библиотеки лежат отдельно и подгружаются только агенту.&lt;h4&gt;Почему именно RPM под EL8, а не EL9&lt;/h4&gt;&lt;p&gt;EL9 идёт уже на OpenSSL 3 — казалось бы, ближе к Photon 4. Но Zabbix Agent 2 для EL9 собран против более свежего glibc (2.34+), а в Photon OS 4 — glibc 2.32. Бинарь EL9 на Photon 4 просто не запустится из-за несовместимых символов glibc. Сборка EL8 (glibc 2.28) совместима, поэтому единственное, что приходится «донести» — это OpenSSL 1.1 и PCRE2.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;О версиях пакетов&lt;/strong&gt;&lt;p align=left&gt;Конкретные версии RPM ниже актуальны на момент написания (апрель 2026). Перед развёртыванием возьмите свежие версии из тех же источников: Zabbix — repo.zabbix.com/zabbix/7.0/rhel/8/x86_64/, openssl-libs 1.1 и pcre2 — pkgs.org или зеркала Rocky/AlmaLinux 8. Цепочка зависимостей и пути сборки от версии к версии не меняются.&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h2&gt;Часть 1. Подготовка&lt;/h2&gt;&lt;p&gt;Подготовиться можно как на самом appliance (если у него есть интернет), так и на соседней Linux-машине. Все артефакты собираем в /root/zbx-portable.&lt;h4&gt;1.1. Скачиваем RPM&lt;/h4&gt;&lt;p&gt;•       zabbix-agent2 RPM под EL8 — с &lt;a href=https://repo.zabbix.com/zabbix/7.0/rhel/8/x86_64/&gt;https://repo.zabbix.com/zabbix/7.0/rhel/8/x86_64/&lt;/a&gt;&lt;p&gt;•       openssl-libs 1.1.x под EL8 — например, с &lt;a href=http://pkgs.org&gt;pkgs.org&lt;/a&gt;&lt;p&gt;•       pcre2 под EL8 — там же&lt;p&gt;Раскладываем в ./rpm/:&lt;pre&gt;&lt;code&gt;/root/zbx-portable/rpm/&#xA;  openssl-libs-1.1.1k-15.el8_6.x86_64.rpm&#xA;  pcre2-10.32-3.el8_6.x86_64.rpm&#xA;  zabbix-agent2-7.0.23-release1.el8.x86_64.rpm&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;1.2. Полностью распаковываем пакет с агентом&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;cd /root/zbx-portable&#xA;rpm2cpio ./rpm/zabbix-agent2-*.rpm | cpio -idmv&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;1.3. Извлекаем только нужные библиотеки&lt;/h4&gt;&lt;p&gt;Из пакетов с библиотеками вытаскиваем только нужные .so и складываем в ./libs/. Для систем с включённым FIPS (vCSA) дополнительно извлекаем .hmac-файлы — без них OpenSSL в FIPS-режиме откажется грузиться:&lt;pre&gt;&lt;code&gt;mkdir -p /root/zbx-portable/libs &amp;amp;&amp;amp; cd /root/zbx-portable/libs&#xA; &#xA;rpm2cpio /root/zbx-portable/rpm/pcre2-10.32-3.el8_6.x86_64.rpm \&#xA;  | cpio -idmv &amp;#39;./usr/lib64/libpcre2-8.so.0*&amp;#39;&#xA; &#xA;rpm2cpio /root/zbx-portable/rpm/openssl-libs-1.1.1k-15.el8_6.x86_64.rpm \&#xA;  | cpio -idmv &amp;#39;./usr/lib64/libssl.so.1.1*&amp;#39; &amp;#39;./usr/lib64/libcrypto.so.1.1*&amp;#39;&#xA; &#xA;# Только для vCSA / FIPS-режима:&#xA;rpm2cpio /root/zbx-portable/rpm/openssl-libs-1.1.1k-15.el8_6.x86_64.rpm | cpio -idmv \&#xA;  &amp;#39;./usr/lib64/.libcrypto.so.1.1.1k.hmac&amp;#39; \&#xA;  &amp;#39;./usr/lib64/.libcrypto.so.1.1.hmac&amp;#39; \&#xA;  &amp;#39;./usr/lib64/.libssl.so.1.1.1k.hmac&amp;#39; \&#xA;  &amp;#39;./usr/lib64/.libssl.so.1.1.hmac&amp;#39;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Обратите внимание на символьные ссылки в полученных файлах — cpio сохранит их, переносить файлы дальше нужно с флагами, сохраняющими симлинки (cp -a / rsync -a).&lt;h2&gt;Часть 2. Установка на Photon OS 4&lt;/h2&gt;&lt;h3&gt;2.1. Системный пользователь и каталоги&lt;/h3&gt;&lt;p&gt;Создаём пользователя и группу для агента:&lt;pre&gt;&lt;code&gt;groupadd --system zabbix&#xA;useradd --system -g zabbix -d /usr/lib/zabbix -s /sbin/nologin \&#xA;  -c &amp;#34;Zabbix Monitoring System&amp;#34; zabbix&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Каталоги для логов и runtime:&lt;pre&gt;&lt;code&gt;mkdir /var/log/zabbix&#xA;chown -R zabbix:zabbix /var/log/zabbix&#xA; &#xA;mkdir /run/zabbix&#xA;chown zabbix:zabbix /run/zabbix&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;2.2. Конфиги и бинарь агента&lt;/h3&gt;&lt;p&gt;Копируем конфиги Zabbix:&lt;pre&gt;&lt;code&gt;cd /root/zbx-portable/&#xA;cp -r etc/zabbix/ /etc/zabbix/&#xA;chown -R root:zabbix /etc/zabbix&#xA;chmod 755 /etc/zabbix&#xA;chmod -R 755 /etc/zabbix/zabbix_agent2.d&#xA;chmod 640 /etc/zabbix/zabbix_agent2.conf&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Теперь правим /etc/zabbix/zabbix_agent2.conf — Server, ServerActive, Hostname и т. д.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Согласуйте PidFile с RuntimeDirectory&lt;/strong&gt;&lt;p align=left&gt;В юните дальше указывается RuntimeDirectory=zabbix → systemd создаст /run/zabbix при старте. Параметр PidFile в zabbix_agent2.conf должен указывать туда же: PidFile=/run/zabbix/zabbix_agent2.pid. Иначе агент будет писать pid в путь по умолчанию, а systemd будет очищать другой каталог — рассинхрон при рестартах.&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Копируем бинарник агента:&lt;pre&gt;&lt;code&gt;cp usr/sbin/zabbix_agent2 /usr/sbin/zabbix_agent2&#xA;chown root:root /usr/sbin/zabbix_agent2&#xA;chmod 755 /usr/sbin/zabbix_agent2&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;2.3. Библиотеки в отдельном каталоге&lt;/h3&gt;&lt;p&gt;Копируем извлечённые .so с сохранением симлинков:&lt;pre&gt;&lt;code&gt;mkdir /etc/zabbix/libs&#xA;cp -a /root/zbx-portable/libs/. /etc/zabbix/libs/&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;На всех каталогах по пути нужен execute-bit (x), иначе линкер не сможет зайти в директорию:&lt;pre&gt;&lt;code&gt;chmod 755 /etc/zabbix&#xA;chmod 755 /etc/zabbix/libs&#xA;chmod 755 /etc/zabbix/libs/usr&#xA;chmod 755 /etc/zabbix/libs/usr/lib64&#xA; &#xA;chmod 644 /etc/zabbix/libs/usr/lib64/libcrypto.so.1.1.* \&#xA;          /etc/zabbix/libs/usr/lib64/libssl.so.1.1.* \&#xA;          /etc/zabbix/libs/usr/lib64/libpcre2-8.so.0.*&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;2.4. systemd-юнит и LD_LIBRARY_PATH&lt;/h3&gt;&lt;p&gt;Копируем unit-файл из распакованного RPM:&lt;pre&gt;&lt;code&gt;cp usr/lib/systemd/system/zabbix-agent2.service \&#xA;   /usr/lib/systemd/system/zabbix-agent2.service&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Если запустить агента сейчас, получим ошибку:&lt;pre&gt;&lt;code&gt;/usr/sbin/zabbix_agent2: error while loading shared libraries:&#xA;  libpcre2-8.so.0: cannot open shared object file&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Список недостающих библиотек удобно смотреть так:&lt;pre&gt;&lt;code&gt;ldd /usr/sbin/zabbix_agent2 | awk &amp;#39;/not found/{print $1}&amp;#39;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Решение — добавить в секцию [Service] переменную окружения, чтобы библиотеки видела только эта служба, а заодно зафиксировать каталог и режим для pid-файла:&lt;pre&gt;&lt;code&gt;vim /usr/lib/systemd/system/zabbix-agent2.service&#xA; &#xA;[Service]&#xA;Environment=&amp;#34;LD_LIBRARY_PATH=/etc/zabbix/libs/usr/lib64&amp;#34;&#xA;RuntimeDirectory=zabbix&#xA;RuntimeDirectoryMode=0755&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Применяем:&lt;pre&gt;&lt;code&gt;systemctl daemon-reload&#xA;systemctl enable --now zabbix-agent2.service&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;Альтернатива: вшить путь в бинарь через patchelf&lt;/h4&gt;&lt;p&gt;Если вы хотите, чтобы агент находил библиотеки независимо от того, как его запустили (не только через systemd, но и руками для отладки), можно вшить RUNPATH прямо в ELF:&lt;pre&gt;&lt;code&gt;tdnf install -y patchelf   # или принести patchelf тем же способом, что и агента&#xA;patchelf --set-rpath /etc/zabbix/libs/usr/lib64 /usr/sbin/zabbix_agent2&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;После этого LD_LIBRARY_PATH в юните не нужен. Минус — после обновления бинарника агента patchelf нужно применять заново.&lt;h2&gt;Часть 3. Проверка работы&lt;/h2&gt;&lt;h4&gt;3.1. Зависимости закрыты&lt;/h4&gt;&lt;p&gt;Первое, что хочется сделать после правки юнита — запустить &lt;code&gt;ldd&lt;/code&gt; и убедиться, что библиотеки нашлись. Но есть нюанс: &lt;code&gt;ldd&lt;/code&gt; использует переменные окружения &lt;em&gt;текущего шелла&lt;/em&gt;, а &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt; мы выставили только внутри unit-файла. Поэтому «голый» &lt;code&gt;ldd&lt;/code&gt; будет продолжать показывать &lt;a href=http://libssl.so&gt;&lt;code&gt;libssl.so&lt;/code&gt;&lt;/a&gt;&lt;code&gt;.1.1&lt;/code&gt;, &lt;a href=http://libcrypto.so&gt;&lt;code&gt;libcrypto.so&lt;/code&gt;&lt;/a&gt;&lt;code&gt;.1.1&lt;/code&gt; и &lt;code&gt;libpcre2-8.so.0&lt;/code&gt; как &lt;code&gt;not found&lt;/code&gt;, даже если агент при этом прекрасно работает под systemd.&lt;p&gt;Правильная проверка — с тем же &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;, который видит служба:&lt;pre&gt;&lt;code&gt;LD_LIBRARY_PATH=/etc/zabbix/libs/usr/lib64 \&#xA;  ldd /usr/sbin/zabbix_agent2 | awk &amp;#39;/not found/{print $1}&amp;#39;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Должны получить пустой вывод.&lt;h4&gt;3.2. Статус и логи службы&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;systemctl status zabbix-agent2 --no-pager&#xA;journalctl -u zabbix-agent2 -b -n 50 --no-pager&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;3.3. Проверка с Zabbix-сервера&lt;/h4&gt;&lt;p&gt;Запуск процесса ещё не гарантирует, что агент реально отвечает. С хоста Zabbix-сервера или прокси проверяем end-to-end:&lt;pre&gt;&lt;code&gt;zabbix_get -s &amp;lt;appliance-ip&amp;gt; -p 10050 -k agent.ping&#xA;# должно вернуть: 1&#xA;&#xA;zabbix_get -s &amp;lt;appliance-ip&amp;gt; -p 10050 -k agent.version&#xA;# должно вернуть номер версии агента&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Если &lt;code&gt;zabbix_get&lt;/code&gt; упирается в таймаут — проблема либо в firewall (см. Часть 4), либо в параметре &lt;code&gt;Server=&lt;/code&gt; в конфиге агента: хост сервера должен быть в нём явно разрешён.&lt;h2&gt;Часть 4. Открытие порта 10050&lt;/h2&gt;&lt;h3&gt;4.1. Cloud Director Appliance&lt;/h3&gt;&lt;p&gt;В /etc/systemd/scripts/ip4save-vmw добавить правило рядом с однотипными записями:&lt;pre&gt;&lt;code&gt;-A INPUT -p tcp -m state --state NEW -m tcp --dport 10050 -j ACCEPT&#xA; &#xA;# и перезапустить службу:&#xA;service iptables restart&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h3&gt;4.2. vCenter Server Appliance&lt;/h3&gt;&lt;p&gt;Временное правило (действует до перезагрузки):&lt;pre&gt;&lt;code&gt;iptables -I port_filter -p tcp --dport 10050 -j ACCEPT&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Закрепить правило между перезагрузками — добавить блок в /etc/vmware/appliance/services.conf:&lt;pre&gt;&lt;code&gt;&amp;#34;zabbix-agent&amp;#34;: {&#xA;    &amp;#34;firewall&amp;#34;: {&#xA;        &amp;#34;enable&amp;#34;: true,&#xA;        &amp;#34;rules&amp;#34;: [&#xA;            {&#xA;                &amp;#34;direction&amp;#34;:  &amp;#34;inbound&amp;#34;,&#xA;                &amp;#34;protocol&amp;#34;:   &amp;#34;tcp&amp;#34;,&#xA;                &amp;#34;porttype&amp;#34;:   &amp;#34;dst&amp;#34;,&#xA;                &amp;#34;port&amp;#34;:       &amp;#34;10050&amp;#34;,&#xA;                &amp;#34;portoffset&amp;#34;: 0&#xA;            }&#xA;        ]&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h2&gt;Часть 5. Сопровождение&lt;/h2&gt;&lt;h4&gt;5.1. Ротация логов&lt;/h4&gt;&lt;p&gt;Из RPM мы скопировали только /etc/zabbix/, без /etc/logrotate.d/. Чтобы лог /var/log/zabbix/zabbix_agent2.log не рос бесконтрольно, добавьте конфиг ротации:&lt;pre&gt;&lt;code&gt;cat &amp;gt; /etc/logrotate.d/zabbix-agent2 &amp;lt;&amp;lt;&amp;#39;EOF&amp;#39;&#xA;/var/log/zabbix/zabbix_agent2.log {&#xA;    weekly&#xA;    rotate 12&#xA;    compress&#xA;    delaycompress&#xA;    missingok&#xA;    notifempty&#xA;    create 0640 zabbix zabbix&#xA;    postrotate&#xA;        /bin/kill -HUP `cat /run/zabbix/zabbix_agent2.pid 2&amp;gt;/dev/null` 2&amp;gt;/dev/null || true&#xA;    endscript&#xA;}&#xA;EOF&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;5.2. Что произойдёт при обновлении appliance&lt;/h4&gt;&lt;pre&gt;&lt;code&gt;ldd /usr/sbin/zabbix_agent2 | awk &amp;#39;/not found/{print $1}&amp;#39;&#xA;systemctl status zabbix-agent2&#xA;zabbix_get -s 127.0.0.1 -k agent.ping&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Если что-то отвалилось — пересоберите /etc/zabbix/libs/ из свежих RPM (см. Часть 1) и при необходимости поднимите версию самого агента.&lt;pre&gt;&lt;code&gt;systemctl disable --now zabbix-agent2.service&#xA;rm -f  /usr/lib/systemd/system/zabbix-agent2.service&#xA;rm -f  /usr/sbin/zabbix_agent2&#xA;rm -rf /etc/zabbix&#xA;rm -rf /var/log/zabbix&#xA;rm -rf /run/zabbix&#xA;userdel zabbix &amp;amp;&amp;amp; groupdel zabbix 2&amp;gt;/dev/null&#xA;systemctl daemon-reload&#xA;# и убрать правило для порта 10050 из firewall (см. Часть 4)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;5.4. AppArmor / SELinux&lt;/h4&gt;&lt;p&gt;Photon OS 4 в стандартной поставке не имеет принудительного MAC-профиля для произвольных бинарей, но если вы вручную включали AppArmor или SELinux — убедитесь, что для /usr/sbin/zabbix_agent2 нет ограничений на чтение /etc/zabbix/libs/. Симптом — агент стартует, но ldd показывает «not found» под пользователем zabbix и не показывает под root.&lt;h2&gt;Итог&lt;/h2&gt;&lt;p&gt;Агент готов: можно подключать пользовательские проверки и плагины — например, для мониторинга PostgreSQL внутри аплайнса. Главное, что мы получили: рабочий Zabbix Agent 2 на Photon OS 4 без вмешательства в системные библиотеки и без установки чужих RPM, то есть без рисков для штатной поддержки appliance со стороны VMware/Broadcom.&lt;br&gt;&lt;br&gt;Следите, что остаётесь с нами! Ваш &lt;a href=&#34;https://www.cloud4y.ru/?utm_source=habr&amp;amp;utm_medium=news&amp;amp;utm_campaign=habr&amp;amp;utm_content=zabbix&amp;amp;utm_term=main&#34;&gt;Cloud4Y&lt;/a&gt;. Читайте нас здесь или в &lt;a href=https://t.me/+ET%5C%5C_qtHM8%5C%5C_540Nzhi&gt;Telegram-канале&lt;/a&gt;!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/cloud4y/articles/1030138/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030138</guid>
      <pubDate>Thu, 30 Apr 2026 12:45:17 +0000</pubDate>
    </item>
    <item>
      <title>Кэширование в Next.js App Router, как увидеть, почему данные не обновились</title>
      <link>https://habr.com/ru/articles/1030134/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030134</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;С кэшированием в Next.js обычно случается одна и та же история. API уже отдаёт новые данные, страница открывается заново, а на экране всё ещё старая версия. После этого в код быстро добавляют &lt;code&gt;cache: &amp;#34;no-store&amp;#34;&lt;/code&gt;, данные начинают запрашиваться на каждый заход, и через пару минут появляется уже другой вопрос - зачем тогда вообще нужен встроенный механизм кэширования.&lt;p&gt;Проблема в том, что кэширование обычно звучит как одно явление, а на практике в App Router похожие ощущения могут давать разные уровни поведения. Навигация назад и вперёд может переиспользовать клиентский кэш маршрута, сам маршрут может рендериться по-разному, а серверный &lt;code&gt;fetch&lt;/code&gt; в Next.js имеет собственные стратегии кэширования и перевалидации. В актуальной документации это уже разделено на новый режим с Cache Components и на прежнюю модель без них. В этой статье речь именно о привычной модели App Router без Cache Components, где поведение обычно задаётся через &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;cache&lt;/code&gt;, &lt;code&gt;next.revalidate&lt;/code&gt; и route segment config. (&lt;a href=https://nextjs.org/docs/app/guides/caching-without-cache-components rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js&lt;/a&gt;)&lt;p&gt;Полезнее всего разбирать такую тему не с теории, а с наблюдения. Не с вопроса как устроены все слои кэша в Next.js, а с вопроса почему на одном и том же маршруте иногда обновляется рендер страницы, а иногда обновляются данные, и это не всегда одно и то же.&lt;p&gt;Для примеров ниже используется проект Goods Finder и внешний API DummyJSON. Идея - сначала добавить на страницу штамп серверного рендера, потом отдельно показать момент получения данных, а уже после этого сравнить &lt;code&gt;force-cache&lt;/code&gt;, &lt;code&gt;no-store&lt;/code&gt; и &lt;code&gt;revalidate&lt;/code&gt;.&lt;h3&gt;Сначала нужно увидеть проблему&lt;/h3&gt;&lt;p&gt;В App Router маршруты по умолчанию рендерятся на сервере, а навигация остаётся client-side. Shared layouts сохраняются, а back/forward могут переиспользовать уже посещённые страницы из client cache. Из-за этого внешне похожие ситуации на самом деле оказываются разными, где-то страница даже не пересчитывалась, а где-то страница пересчиталась, но данные пришли из кэша. (&lt;a href=https://nextjs.org/docs/app/getting-started/linking-and-navigating rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js&lt;/a&gt;)&lt;p&gt;Самый короткий способ добавить на страницу серверный штамп времени.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/_ui/RenderStamp.js&#xA;export default function RenderStamp({ label = &amp;#34;renderedAt&amp;#34; }) {&#xA;  const renderedAt = new Date().toISOString();&#xA;&#xA;  return (&#xA;    &amp;lt;p className=&amp;#34;text-xs text-slate-500&amp;#34;&amp;gt;&#xA;      {label}: &amp;lt;code className=&amp;#34;font-mono&amp;#34;&amp;gt;{renderedAt}&amp;lt;/code&amp;gt;&#xA;    &amp;lt;/p&amp;gt;&#xA;  );&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Этот компонент серверный. Значение вычисляется во время серверного рендера. Если страница реально пересчиталась, время изменится. Если был показан повторный результат, штамп останется прежним.&lt;p&gt;Дальше его удобно подключить на список и на детальную страницу товара.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/(app)/goods/page.js&#xA;import RenderStamp from &amp;#34;@/app/_ui/RenderStamp&amp;#34;;&#xA;&#xA;// ... JSX страницы&#xA;&amp;lt;RenderStamp label=&amp;#34;/goods render&amp;#34; /&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=javascript&gt;// src/app/(app)/goods/[id]/page.js&#xA;import RenderStamp from &amp;#34;@/app/_ui/RenderStamp&amp;#34;;&#xA;&#xA;// ... JSX страницы&#xA;&amp;lt;RenderStamp label=&amp;#34;goods/[id] render&amp;#34; /&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;После этого можно пройти обычный сценарий - открыть &lt;code&gt;/goods&lt;/code&gt;, зайти в карточку товара, нажать Back, снова открыть карточку, обновить страницу. На навигации назад штамп не меняется. На reload он меняется. Уже на этом шаге становится видно, что не каждый повторный показ страницы означает новый серверный проход. То, что pages могут переиспользоваться при back/forward navigation, отдельно зафиксировано и в текущем glossary Next.js. (&lt;a href=https://nextjs.org/docs/app/glossary rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js&lt;/a&gt;)&lt;h3&gt;Один рендер и одни данные это не одно и то же&lt;/h3&gt;&lt;p&gt;Следующий шаг полезнее любого объяснения про Data Cache. Нужно показать не только момент рендера, но и момент, когда пришли сами данные. Для этого в слой данных можно добавить ещё один маркер. В проекте он берётся из HTTP-заголовка &lt;code&gt;Date&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/_data/dummyjson.js&#xA;const API_BASE = &amp;#34;https://dummyjson.com&amp;#34;;&#xA;&#xA;async function fetchJson(url, fetchOptions = {}) {&#xA;  const res = await fetch(url, fetchOptions);&#xA;&#xA;  if (!res.ok) {&#xA;    const text = await res.text().catch(() =&amp;gt; &amp;#34;&amp;#34;);&#xA;    const err = new Error(`DummyJSON error: ${res.status} ${res.statusText}. ${text}`);&#xA;    err.status = res.status;&#xA;    throw err;&#xA;  }&#xA;&#xA;  const fetchedAt = res.headers.get(&amp;#34;date&amp;#34;) || new Date().toUTCString();&#xA;  const data = await res.json();&#xA;&#xA;  data.__fetchedAt = fetchedAt;&#xA;&#xA;  return data;&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Теперь можно вернуть этот маркер на страницу рядом с &lt;code&gt;RenderStamp&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/(app)/goods/[id]/page.js&#xA;&amp;lt;p className=&amp;#34;text-xs text-slate-500&amp;#34;&amp;gt;&#xA;  data fetched: &amp;lt;code className=&amp;#34;font-mono&amp;#34;&amp;gt;{item.__fetchedAt}&amp;lt;/code&amp;gt;&#xA;&amp;lt;/p&amp;gt;&#xA;&amp;lt;RenderStamp label=&amp;#34;goods/[id] render&amp;#34; /&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В этот момент кэширование перестаёт быть абстракцией. Есть два независимых сигнала. Первый показывает, пересчитывалась ли страница. Второй показывает, пришёл ли новый ответ от внешнего API.&lt;p&gt;Здесь становится видно главное, страница может пересчитаться заново, а данные при этом остаться прежними.&lt;h3&gt;force-cache, рендер может меняться, а данные нет&lt;/h3&gt;&lt;p&gt;Теперь можно включить на запросах &lt;code&gt;force-cache&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/_data/dummyjson.js&#xA;export async function getProducts({ q = &amp;#34;&amp;#34;, limit = 12, skip = 0 } = {}) {&#xA;  const safeQ = String(q).trim();&#xA;  const qs = new URLSearchParams({&#xA;    limit: String(limit),&#xA;    skip: String(skip),&#xA;  });&#xA;&#xA;  const url = safeQ&#xA;    ? `${API_BASE}/products/search?${qs.toString()}&amp;amp;q=${encodeURIComponent(safeQ)}`&#xA;    : `${API_BASE}/products?${qs.toString()}`;&#xA;&#xA;  return fetchJson(url, { cache: &amp;#34;force-cache&amp;#34; });&#xA;}&#xA;&#xA;export async function getProductById(id) {&#xA;  const safeId = encodeURIComponent(String(id));&#xA;  const url = `${API_BASE}/products/${safeId}`;&#xA;&#xA;  return fetchJson(url, { cache: &amp;#34;force-cache&amp;#34; });&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В актуальной документации &lt;code&gt;force-cache&lt;/code&gt; описан буквально так - Next.js ищет совпадение в серверном кэше, если запись свежая, она возвращается из кэша, а если записи нет или она устарела, Next.js идёт в источник и обновляет кэш. (&lt;a href=https://nextjs.org/docs/app/api-reference/functions/fetch rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js&lt;/a&gt;)&lt;p&gt;После &lt;code&gt;npm run build &amp;amp;&amp;amp; npm start&lt;/code&gt; можно открыть, например, &lt;code&gt;/goods/1&lt;/code&gt; и сделать несколько обычных обновлений страницы. Поведение выглядит показательно: &lt;code&gt;goods/[id] render&lt;/code&gt; меняется, а &lt;code&gt;data fetched&lt;/code&gt; может оставаться прежним. Страница пересчиталась, но данные были переиспользованы из кэша.&lt;p&gt;Это и есть та ловушка, из-за которой возникает ощущение данные залипли. На самом деле рендер живой, но источник данных не был запрошен заново.&lt;h3&gt;no-store, теперь оба маркера идут вместе&lt;/h3&gt;&lt;p&gt;Дальше достаточно заменить режим запроса:&lt;pre&gt;&lt;code class=javascript&gt;// src/app/_data/dummyjson.js&#xA;return fetchJson(url, { cache: &amp;#34;no-store&amp;#34; });&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Документация определяет &lt;code&gt;no-store&lt;/code&gt; прямо: Next.js запрашивает ресурс у удалённого сервера на каждый запрос, даже если request-time APIs на маршруте не использовались. &lt;p&gt;После пересборки и нескольких обновлений страницы уже видно другое поведение. &lt;code&gt;goods/[id] render&lt;/code&gt; меняется, и &lt;code&gt;data fetched&lt;/code&gt; тоже меняется на каждом обновлении. Здесь рендер и данные идут вместе, потому что сетевой ответ каждый раз новый.&lt;p&gt;С практической точки зрения это удобно для баланса, статусов заказа, личных кабинетов, внутренних панелей и любых данных, где устаревшая версия уже считается ошибкой. Но такой режим не всегда нужен для публичного каталога. Он увеличивает число запросов и нагружает внешний API там, где обычно хватает более долгой стратегии.&lt;h3&gt;revalidate, не всегда свежо, но предсказуемо&lt;/h3&gt;&lt;p&gt;Для каталога, витрины и списков товаров чаще нужен промежуточный режим. Не постоянный сетевой запрос и не бесконечное переиспользование кэша, а окно свежести. В Next.js это делается через &lt;code&gt;next.revalidate&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/_data/dummyjson.js&#xA;const REVALIDATE_SECONDS = 15;&#xA;&#xA;export async function getProductById(id) {&#xA;  const safeId = encodeURIComponent(String(id));&#xA;  const url = `${API_BASE}/products/${safeId}`;&#xA;&#xA;  return fetchJson(url, { next: { revalidate: REVALIDATE_SECONDS } });&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В официальной документации &lt;code&gt;next.revalidate&lt;/code&gt; задаёт lifetime ресурса в секундах. &lt;code&gt;0&lt;/code&gt; отключает кэширование, положительное число задаёт максимальное окно жизни записи, а &lt;code&gt;false&lt;/code&gt; семантически эквивалентен бесконечному хранению. Чтобы этот режим было легче наблюдать, полезно добавить ещё один маркер &lt;code&gt;freshUntil&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// внутри fetchJson&#xA;const revalidateSec = fetchOptions?.next?.revalidate;&#xA;const fetchedAtMs = Date.parse(fetchedAt);&#xA;&#xA;const freshUntil =&#xA;  Number.isFinite(fetchedAtMs) &amp;amp;&amp;amp; Number.isFinite(revalidateSec)&#xA;    ? new Date(fetchedAtMs + revalidateSec * 1000).toUTCString()&#xA;    : &amp;#34;&amp;#34;;&#xA;&#xA;const data = await res.json();&#xA;data.__fetchedAt = fetchedAt;&#xA;data.__revalidateSec = Number.isFinite(revalidateSec) ? revalidateSec : null;&#xA;data.__freshUntil = freshUntil;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;И вывести это рядом с рендер-штампом.&lt;pre&gt;&lt;code class=javascript&gt;&amp;lt;p className=&amp;#34;text-xs text-slate-500&amp;#34;&amp;gt;&#xA;  data fetched: &amp;lt;span className=&amp;#34;font-mono&amp;#34;&amp;gt;{item.__fetchedAt}&amp;lt;/span&amp;gt;&#xA;  {item.__revalidateSec ? (&#xA;    &amp;lt;&amp;gt;&#xA;      &amp;lt;span className=&amp;#34;text-slate-300&amp;#34;&amp;gt; • &amp;lt;/span&amp;gt;&#xA;      revalidate: &amp;lt;span className=&amp;#34;font-mono&amp;#34;&amp;gt;{item.__revalidateSec}s&amp;lt;/span&amp;gt;&#xA;      &amp;lt;span className=&amp;#34;text-slate-300&amp;#34;&amp;gt; • &amp;lt;/span&amp;gt;&#xA;      fresh until: &amp;lt;span className=&amp;#34;font-mono&amp;#34;&amp;gt;{item.__freshUntil}&amp;lt;/span&amp;gt;&#xA;    &amp;lt;/&amp;gt;&#xA;  ) : null}&#xA;&amp;lt;/p&amp;gt;&#xA;&amp;lt;RenderStamp label=&amp;#34;goods/[id] render&amp;#34; /&amp;gt;&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Пока запросы укладываются в окно свежести, &lt;code&gt;render&lt;/code&gt; может обновляться, а &lt;code&gt;data fetched&lt;/code&gt; остаётся прежним. После выхода за окно свежести кэш начинает обновляться, и &lt;code&gt;data fetched&lt;/code&gt; меняется. В документации это и описано как time-based revalidation через &lt;code&gt;fetch(..., { next: { revalidate: N } })&lt;/code&gt;. Там же отдельно отмечено, что route-level &lt;code&gt;revalidate&lt;/code&gt; не переопределяет более частую revalidation у отдельных &lt;code&gt;fetch&lt;/code&gt; внутри маршрута.&lt;h3&gt;Как увидеть именно Data Cache, а не всё сразу&lt;/h3&gt;&lt;p&gt;На практике у новичка здесь часто ломается наблюдение. Он добавляет &lt;code&gt;revalidate&lt;/code&gt;, обновляет страницу и не понимает, какой именно слой сейчас сработал. Чтобы эксперимент был чище, в проекте есть полезный приём - отключить Full Route Cache для сегмента, но оставить Data Cache у &lt;code&gt;fetch&lt;/code&gt;. Это можно сделать через request-time API &lt;code&gt;headers()&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/(app)/goods/[id]/page.js&#xA;import { headers } from &amp;#34;next/headers&amp;#34;;&#xA;&#xA;export default async function GoodsDetailsPage({ params }) {&#xA;  const p = await params;&#xA;  const item = await getProductById(p.id);&#xA;&#xA;  headers();&#xA;&#xA;  return (&#xA;    &amp;lt;div&amp;gt;&#xA;      &amp;lt;h1&amp;gt;{item.title}&amp;lt;/h1&amp;gt;&#xA;      &amp;lt;RenderStamp label=&amp;#34;goods/[id] render&amp;#34; /&amp;gt;&#xA;    &amp;lt;/div&amp;gt;&#xA;  );&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В текущей документации &lt;code&gt;headers()&lt;/code&gt; описан как Request-time API. Его использование переводит маршрут в dynamic rendering. В guide по прежней модели кэширования также зафиксировано, что &lt;code&gt;dynamic = &amp;#34;force-dynamic&amp;#34;&lt;/code&gt; эквивалентен режиму, где все &lt;code&gt;fetch&lt;/code&gt; внутри страницы ведут себя как &lt;code&gt;cache: &amp;#39;no-store&amp;#39;&lt;/code&gt; и &lt;code&gt;revalidate: 0&lt;/code&gt;. В той же guide отмечено, что по умолчанию &lt;code&gt;fetch&lt;/code&gt; до request-time APIs может кэшироваться, а после них нет. (&lt;a href=https://nextjs.org/docs/app/api-reference/functions/headers rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js&lt;/a&gt;)&lt;p&gt;Иногда проблема не в том, что режим кэширования выбран неправильно. Проблема в том, что разные уровни кэша смешались, и разработчик наблюдает всё сразу.&lt;h3&gt;Почему в dev всё ощущается иначе&lt;/h3&gt;&lt;p&gt;В development поведение действительно отличается. В актуальной документации у &lt;code&gt;fetch&lt;/code&gt; прямо сказано, что режим по умолчанию &lt;code&gt;auto no cache&lt;/code&gt; в dev запрашивает ресурс на каждый запрос, но во время &lt;code&gt;next build&lt;/code&gt; статически подготавливаемый маршрут может получить этот ресурс один раз. Там же указано, что в local development у Server Components есть HMR cache, который по умолчанию распространяется даже на &lt;code&gt;cache: &amp;#39;no-store&amp;#39;&lt;/code&gt;. Из-за этого uncached requests могут не показывать свежие данные между HMR refreshes. Этот кэш очищается при навигации или полном reload. Дополнительно hard refresh и отключённый cache в DevTools часто посылают &lt;code&gt;cache-control: no-cache&lt;/code&gt;, и тогда &lt;code&gt;cache&lt;/code&gt;, &lt;code&gt;revalidate&lt;/code&gt; и &lt;code&gt;tags&lt;/code&gt; могут быть проигнорированы, а запрос пойдёт прямо в источник.&lt;p&gt;Именно поэтому кэширование в Next.js лучше проверять не только в &lt;code&gt;next dev&lt;/code&gt;, а обязательно через &lt;code&gt;npm run build &amp;amp;&amp;amp; npm start&lt;/code&gt;. Для этой темы production-режим не просто финальная проверка, а часть самой диагностики.&lt;h3&gt;Cache-lab как нормальный способ проверить режимы&lt;/h3&gt;&lt;p&gt;Чтобы не переключать код вручную между &lt;code&gt;force-cache&lt;/code&gt;, &lt;code&gt;no-store&lt;/code&gt; и &lt;code&gt;revalidate&lt;/code&gt;, в Goods Finder была вынесена отдельная лаборатория &lt;code&gt;/cache-lab&lt;/code&gt;. Режим кэша передаётся через query-параметры, слой данных принимает профиль кэша вторым аргументом, а список и карточка товара показывают рядом &lt;code&gt;render&lt;/code&gt;, &lt;code&gt;data fetched&lt;/code&gt;, &lt;code&gt;revalidate&lt;/code&gt; и &lt;code&gt;fresh until&lt;/code&gt;.&lt;pre&gt;&lt;code class=javascript&gt;// src/app/_data/dummyjson.js&#xA;function toCacheOptions(profile) {&#xA;  const mode = profile?.mode || &amp;#34;force-cache&amp;#34;;&#xA;  const n = Number(profile?.n);&#xA;&#xA;  if (mode === &amp;#34;no-store&amp;#34;) return { cache: &amp;#34;no-store&amp;#34; };&#xA;&#xA;  if (mode === &amp;#34;revalidate&amp;#34;) {&#xA;    const revalidate = Number.isFinite(n) &amp;amp;&amp;amp; n &amp;gt; 0 ? n : 15;&#xA;    return { next: { revalidate } };&#xA;  }&#xA;&#xA;  return { cache: &amp;#34;force-cache&amp;#34; };&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;pre&gt;&lt;code class=javascript&gt;export async function getProductById(id, cacheProfile) {&#xA;  const safeId = encodeURIComponent(String(id));&#xA;  const url = `${API_BASE}/products/${safeId}`;&#xA;&#xA;  return fetchJson(url, toCacheOptions(cacheProfile));&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Такой стенд полезен по двум причинам. Во-первых, он даёт наглядную витрину для самой темы кэширования. Во-вторых, не засоряет боевой интерфейс &lt;code&gt;/goods&lt;/code&gt; и &lt;code&gt;/goods/[id]&lt;/code&gt;, где учебные маркеры в итоговой версии уже не нужны.&lt;h3&gt;Как выбрать режим без долгой теории&lt;/h3&gt;&lt;p&gt;Если данные должны быть свежими на каждый запрос, подходит &lt;code&gt;no-store&lt;/code&gt;. Это история про баланс, статусы, динамические внутренние панели и всё, где устаревший ответ уже мешает.&lt;p&gt;Если данные меняются редко и источник нужно щадить, подходит &lt;code&gt;force-cache&lt;/code&gt;. Это справочники, коллекции, публичные списки и всё, где небольшая задержка обновления допустима.&lt;p&gt;Если нужен рабочий компромисс для каталога, витрины или списка товаров, подходит &lt;code&gt;next: { revalidate: N }&lt;/code&gt;. Именно этот вариант чаще всего оказывается базовым решением для публичных страниц. В guide по прежней модели кэширования route-level &lt;code&gt;revalidate = false&lt;/code&gt; описан как поведение, близкое к бесконечному хранению для кэшируемых fetch, а положительное число задаёт частоту перевалидации маршрута, отдельные &lt;code&gt;fetch&lt;/code&gt; могут уменьшать этот интервал. (&lt;a href=https://nextjs.org/docs/app/api-reference/functions/fetch rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js&lt;/a&gt;)&lt;p&gt;Для Goods Finder практическим выбором стал режим &lt;code&gt;revalidate: 60&lt;/code&gt;. Для каталога товаров это нормальный баланс между свежестью данных и числом запросов к внешнему API.&lt;h3&gt;В итоге&lt;/h3&gt;&lt;p&gt;Главная польза кэширования в Next.js начинается в момент, когда становится видно, что рендер страницы и свежесть данных это разные вещи. Один &lt;code&gt;RenderStamp&lt;/code&gt; показывает, пересчиталась ли страница. Один &lt;code&gt;data fetched&lt;/code&gt; показывает, пришёл ли новый ответ. После этого &lt;code&gt;force-cache&lt;/code&gt;, &lt;code&gt;no-store&lt;/code&gt; и &lt;code&gt;revalidate&lt;/code&gt; перестают быть абстрактными флагами и превращаются в обычные инженерные решения с понятным поведением.&lt;p&gt;Статья опирается на проект &lt;a href=https://goods-finder-js.onrender.com/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Goods Finder&lt;/a&gt;. Пройти эти паттерны последовательно можно на курсе &lt;a href=https://stepik.org/a/272213 rel=&#34;noopener noreferrer nofollow&#34;&gt;Next.js I: JavaScript 2026&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>lemon_m</author>
      <guid>https://habr.com/ru/articles/1030134/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030134</guid>
      <pubDate>Thu, 30 Apr 2026 12:28:25 +0000</pubDate>
    </item>
    <item>
      <title>Как мы автоматизировали маркетинг, продажи и контроль стеком ИИ-агентов</title>
      <link>https://habr.com/ru/articles/1030132/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030132</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;В нашей компании нет отдела маркетинга. Нет руководителя продаж. Нет SMM-щика, копирайтера, аналитика и таргетолога. Есть один CEO и стек из 12 ИИ-агентов, которые ведут восемь каналов коммуникации, контролируют менеджеров по продажам, мониторят расходы на рекламу 24/7, пишут персональные письма клиентам с цитатами из их звонков, и каждое утро в 6:00 МСК сами обучаются на ночной переписке команды.&lt;p&gt;Эта статья — разбор без купюр: как это устроено, что работает, где грабли. В конце — отдельный блок про применимость в любом бизнесе.&lt;p&gt;Никаких выдуманных диалогов и скриншотов «для презентации». Всё ниже — реальные сообщения из нашего рабочего Telegram-чата за последние две недели. С точными временами, реальными ID, и теми багами, которые мы публично признавали в той же переписке.&lt;h4&gt;Кейс №1. «Ром, убери» — 9 минут от запроса до фикса&lt;/h4&gt;&lt;p&gt;Менеджер Юля видит в хелпике CRM описание услуги, которая не работает, и пишет в общий чат с прикреплённым скриншотом: «уберите тогда пункт публикация на порталы».&lt;p&gt;CEO одной строкой даёт команду в чат: «Ром, можешь найти этот текст и убрать полностью первый пункт. Будет только один пункт 2».&lt;p&gt;Через 9 минут ИИ-ассистент отвечает: «Готово. Убрал первый пункт из хелпика — теперь там только один раздел про XML/ФИД. Обновлено в двух версиях хелпика (актуальной и предыдущей). Кеш сброшен — изменения видны сразу.»&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/f3d/119/45e/f3d11945e38c18e1b1255546eb8994c1.png alt=&#34;Запрос CEO в чате и ответ ИИ-ассистента&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/f3d/119/45e/f3d11945e38c18e1b1255546eb8994c1.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/f3d/119/45e/f3d11945e38c18e1b1255546eb8994c1.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Запрос CEO в чате и ответ ИИ-ассистента&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В классической схеме это: тикет от менеджера → разработчик в спринт → деплой на следующей неделе. Здесь — одно сообщение в чат и фикс в двух версиях текста с автоматическим сбросом кеша. Девять минут.&lt;h4&gt;Кейс №2. Статья родилась прямо в переписке&lt;/h4&gt;&lt;p&gt;Утром менеджер пишет в чат: «Тестили нашу CRM, остановились на Битриксе. Конкретно по цене не знаю, но знаю, что Б24 очень тяжело настраивается под свои бизнес-процессы». Это сигнал: компания теряет клиентов из-за сравнения с универсальными CRM.&lt;p&gt;На следующее утро CEO в том же чате анализирует ситуацию: «Я посмотрел глубже, и у Bitrix24 «реальная стоимость» сильно отличается от цены на витрине. Что у них есть скрытые расходы». И дальше — детальный разбор скрытых тарифов с источниками.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/42e/3b7/ebd/42e3b7ebd879bd2dac0f175e23a99f13.png alt=&#34;Цитата менеджера и анализ CEO в Telegram-чате&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/42e/3b7/ebd/42e3b7ebd879bd2dac0f175e23a99f13.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/42e/3b7/ebd/42e3b7ebd879bd2dac0f175e23a99f13.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Цитата менеджера и анализ CEO в Telegram-чате&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;ИИ-директор маркетинга в этот же тик читает чат, классифицирует это как сигнал на контент и сам — без явного запроса — формирует Decision: «статья «Специализированная CRM для недвижимости vs универсальная — что выгоднее»; сигнал: дискуссия CEO о Б24».&lt;p&gt;Через 1 час 28 минут после первой реплики CEO статья на 22 550 символов уже опубликована на сайте, появились анонсы в пяти каналах: Telegram, ВКонтакте, Дзен, Max, LinkedIn.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/792/1b3/357/7921b3357fab3ea6e7f90c857881c30c.png alt=&#34;Готовая статья на сайте — итог дискуссии в чате&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/792/1b3/357/7921b3357fab3ea6e7f90c857881c30c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/792/1b3/357/7921b3357fab3ea6e7f90c857881c30c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Готовая статья на сайте — итог дискуссии в чате&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Эту статью не написал маркетолог — её написал ИИ-директор по сигналу из переписки.&lt;h4&gt;Кейс №3. Один пост — пять каналов за минуту&lt;/h4&gt;&lt;p&gt;12:03. Один материал — экс-премьер Степашин о вторичном рынке — публикуется параллельно в пять платформ. Ниже — четыре скриншота из Telegram, ВКонтакте, Max и Дзена. Время публикации — с 12:03 по 12:06.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/b82/c8e/de2/b82c8ede205a4cb85616affb92571476.png alt=&#34;Пост в Telegram&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/b82/c8e/de2/b82c8ede205a4cb85616affb92571476.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/b82/c8e/de2/b82c8ede205a4cb85616affb92571476.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Пост в Telegram&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/70f/253/12a/70f25312a75b265f86e9622ac4f7ecfd.png alt=&#34;Пост ВКонтакте&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/70f/253/12a/70f25312a75b265f86e9622ac4f7ecfd.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/70f/253/12a/70f25312a75b265f86e9622ac4f7ecfd.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Пост ВКонтакте&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/af3/021/e4d/af3021e4d910584567bce7af72ee4b2e.png alt=&#34;Пост в Max&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/af3/021/e4d/af3021e4d910584567bce7af72ee4b2e.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/af3/021/e4d/af3021e4d910584567bce7af72ee4b2e.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Пост в Max&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/7f4/60e/ab1/7f460eab15827afa9dfc4bfc9f746e63.png alt=&#34;Пост в Дзене&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/7f4/60e/ab1/7f460eab15827afa9dfc4bfc9f746e63.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/7f4/60e/ab1/7f460eab15827afa9dfc4bfc9f746e63.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Пост в Дзене&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Все четыре поста — одинаковая структура: заголовок, картинка, маркированный список «что это значит для риэлтора», вывод. Адаптацию под формат каждой платформы делает соответствующий субагент. Картинку для всех каналов один раз сгенерировал агент-художник.&lt;h4&gt;Кейс №4. Каждое утро — детальная карточка по каждому менеджеру продаж&lt;/h4&gt;&lt;p&gt;Каждый рабочий день в 12:00 МСК ИИ-агент собирает все звонки менеджеров продаж длиннее 10 секунд за прошедшие сутки, проводит распознавание через STT-сервис, подтягивает переписку из мессенджеров, и публикует в группу руководителей пять сообщений: одно сводное и четыре персональные карточки — по одной на каждого менеджера.&lt;p&gt;Так выглядит дневная карточка менеджера — с метриками, повторяющимися ошибками за 14 дней, рекомендацией на день и проверкой соблюдения внутреннего регламента:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/65c/c5d/66a/65cc5d66a1394a73fe5a66539dc7d1dd.png alt=&#34;Дневная карточка менеджера&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/65c/c5d/66a/65cc5d66a1394a73fe5a66539dc7d1dd.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/65c/c5d/66a/65cc5d66a1394a73fe5a66539dc7d1dd.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Дневная карточка менеджера&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;За пять секунд чтения руководитель понимает: спокойное утро, один системный паттерн (шаблонный скрипт без адаптации, 25 повторов за две недели — красный флаг), конкретная задача на день («раскрывать причину отказа», а не «закрывать сразу после “нет, спасибо”»), нарушений нет.&lt;p&gt;Это работа РОПа со стажем 10 лет. Каждый день. По четверым менеджерам сразу.&lt;h4&gt;Кейс №5. Менеджер задал вопрос — ИИ дал готовый ответ из БД&lt;/h4&gt;&lt;p&gt;Менеджер в общем чате: «Ром, посмотри какие действия делает аккаунт N, до этого говорил, что не нуждается в услугах».&lt;p&gt;Через несколько минут ИИ возвращает разбор:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/001/6d4/676/0016d4676a4103cddf6e458891b85059.png alt=&#34;Продуктовая диагностика клиента по запросу менеджера&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/001/6d4/676/0016d4676a4103cddf6e458891b85059.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/001/6d4/676/0016d4676a4103cddf6e458891b85059.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Продуктовая диагностика клиента по запросу менеджера&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Что сделал ИИ за это время: подцепился к продуктовой БД, нашёл аккаунт по номеру телефона, поднял профиль клиента, посчитал визиты с момента отказа от услуг (15 раз с 11 марта), посчитал каждое осмысленное действие в системе (ручных поисков 0, открытий телефонов 0, просмотров карточек 0), и сформулировал вывод человеческим языком: «он реально ничего не делает, просто заглядывает по привычке или смотрит “что нового”». В конце — связь с предыдущей задачей менеджера, которая теперь подтверждается данными.&lt;p&gt;В классической схеме менеджер бы пошёл в техподдержку. Техподдержка — к аналитику. Аналитик — к разработчику с просьбой написать SQL-запрос. Через два дня менеджер получит выгрузку, которую ему ещё нужно интерпретировать. Здесь — три минуты от вопроса в чате до готового ответа с трактовкой.&lt;h4&gt;Кейс №6. Триггерные письма с цитатами из звонков&lt;/h4&gt;&lt;p&gt;Когда клиент регистрируется и общается с менеджером, его звонки автоматически расшифровываются. ИИ хранит транскрипции 90 дней. Если клиент после звонка пропадает на 3 дня — триггерный агент читает все его звонки, понимает контекст, и сам пишет персональное письмо.&lt;p&gt;Пример настоящего письма, отправленного 27 апреля:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/770/6fb/3f7/7706fb3f7b4913345bf9ad7af70ad88c.png alt=&#34;Триггерное email-письмо с упоминанием контекста из звонка&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/770/6fb/3f7/7706fb3f7b4913345bf9ad7af70ad88c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/770/6fb/3f7/7706fb3f7b4913345bf9ad7af70ad88c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Триггерное email-письмо с упоминанием контекста из звонка&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Здесь ИИ:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Помнит, что половина офиса клиента болеет — это было сказано в звонке&lt;li&gt;&lt;p&gt;Помнит, что менеджер обещал перенести встречу на 30 апреля&lt;li&gt;&lt;p&gt;Достал из логов, что клиент сделал 656 действий в системе — даёт это как валидацию: «значит, основное уже попробовали»&lt;li&gt;&lt;p&gt;Зашил конкретную полезную ссылку на инструкцию по автопубликации, релевантную текущему этапу&lt;/ul&gt;&lt;p&gt;Менеджер на такое письмо тратил бы 30 минут на каждого клиента. У нас регистрируются десятки в день. Один менеджер физически не успевает писать персональные касания всем — поэтому раньше клиенты получали или шаблоны, или ничего. Теперь — персональные письма с реальным контекстом.&lt;h4&gt;Кейс №7. Утренний дайджест: что происходит в компании&lt;/h4&gt;&lt;p&gt;Каждое рабочее утро в 12:07 МСК ИИ-агент публикует в общем чате компании дайджест за прошедший день. Это — ежедневная аналитика, которую в обычной компании готовит сотрудник.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/571/620/e3e/571620e3ec49371fc9f985a143dfb888.png alt=&#34;Утренний дайджест ИИ-бота в Telegram-чате&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/571/620/e3e/571620e3ec49371fc9f985a143dfb888.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/571/620/e3e/571620e3ec49371fc9f985a143dfb888.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Утренний дайджест ИИ-бота в Telegram-чате&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В одном сообщении: динамика регистраций по дням за неделю, рекламные расходы и ROI, разбор каждого рекламного лида (статус из звонка, рекомендуемое действие), список горячих клиентов дня с конкретными причинами «почему горячий», и проверка соблюдения внутреннего регламента менеджерами.&lt;p&gt;Никто не сидит ночью и не готовит этот отчёт. ИИ собирает данные из четырёх источников (CRM, биллинг, метрика, телефония), классифицирует, расставляет приоритеты, и публикует.&lt;h4&gt;Кейс №8. ИИ сам нашёл функцию в коде и предложил статью&lt;/h4&gt;&lt;p&gt;Утром ИИ-директор делает регулярный обход git-коммитов нашей CRM. Замечает: модуль &lt;code&gt;photo-editor&lt;/code&gt; разросся до 28 функций. На сайте описан только в общих чертах. Это контентный пробел.&lt;p&gt;В чат CEO падает Decision-карточка с предложением:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/c5d/034/d70/c5d034d704e8cca96bc3d0ee0cf37494.png alt=&#34;Decision-карточка ИИ-директора о статье на основе анализа кода CRM&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/c5d/034/d70/c5d034d704e8cca96bc3d0ee0cf37494.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/c5d/034/d70/c5d034d704e8cca96bc3d0ee0cf37494.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Decision-карточка ИИ-директора о статье на основе анализа кода CRM&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В одной карточке: тема, цели, приоритет, источник идеи («исследовал код CRM: 28 функций»), список каналов публикации (site, dzen, telegram, vk, max), и три кнопки для CEO — «одобрить → сразу запущу исполнение», «отклонить», «процитируй и напиши правки → сохраню как урок».&lt;p&gt;CEO одобряет. Через 2 часа — статья опубликована на сайте, посты во всех каналах. Иллюстрации к статье — реальные скриншоты CRM, ИИ их сам сделал через скриншотный сервис, использовав внутренние логины.&lt;h4&gt;Кейс №9. Как ИИ закрыл слив бюджета 86 622 ₽ в Яндекс.Директе&lt;/h4&gt;&lt;p&gt;Каждый час ИИ-директор делает ads-snapshot — фиксирует расход и конверсии Яндекс.Директа. Когда автоматическая стратегия Директа начала резать показы из-за нулевых конверсий, кампания фактически умерла. Бюджет тратится на единичные клики, регистраций нет.&lt;p&gt;На седьмой день ИИ присылает CEO в личку полный ROI-анализ за три месяца:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/514/74f/42b/51474f42b6a4011f16398baf6c9a5cb3.png alt=&#34;ROI-анализ от ИИ-директора и решение CEO остановить рекламу&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/514/74f/42b/51474f42b6a4011f16398baf6c9a5cb3.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/514/74f/42b/51474f42b6a4011f16398baf6c9a5cb3.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;ROI-анализ от ИИ-директора и решение CEO остановить рекламу&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Цифры жёсткие: потрачено 86 622 ₽, регистраций 87 (CPA 996 ₽), оплат 0, ROI = -100%. Для сравнения ИИ приводит органику: 833 регистрации, 35 оплат, конверсия 4,2%.&lt;p&gt;Вердикт: «Кампания мертва 8-й день. Рекомендация: остановить. Нужно решение.» CEO отвечает «Останавливаем!». Директ выключен.&lt;p&gt;Главное здесь — не цифры, а то, что без ИИ это просто бы не произошло. Ни один человек в маленькой компании не сидит и не считает ROI Директа за три месяца с разбивкой по неделям и сравнением с органикой. Это работа аналитика. У нас аналитик — это часовой cron, который читает API Метрики, биллинг и Директ, и раз в неделю подсвечивает аномалии CEO в личку.&lt;h4&gt;Кейс №10. Как ИИ дебажит свой собственный стек&lt;/h4&gt;&lt;p&gt;Самый неожиданный — и для нас самый показательный — кейс. CEO замечает, что в отчётах менеджерам время указывается со сдвигом. Пишет ИИ: «разберись».&lt;p&gt;Через несколько часов ИИ возвращается с диагнозом:&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/13d/c60/2c6/13dc602c65a6253b03c007d7269583d7.png alt=&#34;ИИ нашёл root cause бага с таймзонами в собственном коде&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/13d/c60/2c6/13dc602c65a6253b03c007d7269583d7.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/13d/c60/2c6/13dc602c65a6253b03c007d7269583d7.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;ИИ нашёл root cause бага с таймзонами в собственном коде&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Что произошло: сервер в UTC, CRM хранит время в МСК. Скрипты, которые читали время сервера через стандартный &lt;code&gt;datetime.now()&lt;/code&gt;, получали UTC и сдвигали все события на 3 часа назад. Из-за этого:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Менеджер видела в утреннем отчёте «звонок в 13:28», хотя реально звонила в 10:15&lt;li&gt;&lt;p&gt;Расчёт «рабочих часов» ломался — 8:00 UTC интерпретировалось как 11:00 МСК&lt;li&gt;&lt;p&gt;Расписание ежедневных задач срабатывало не в то время&lt;/ul&gt;&lt;p&gt;Самое интересное — что было дальше. ИИ нашёл root cause, создал единый модуль &lt;code&gt;now_msk()&lt;/code&gt;, заменил все 60+ старых вызовов в 20 файлах, и в том же сообщении публично признал ошибку: «Менеджеры правы — цифры в том отчёте были неверные. Претензии к задержкам в понедельник были ошибочными». Никто из менеджеров об этом не просил. ИИ сам инициировал correction.&lt;p&gt;Это работа архитектора-разработчика с правом коммитить в продакшн. У нас этим архитектором стала маленькая обвязка вокруг LLM с доступом к коду.&lt;h4&gt;Что НЕ автоматизировано — и почему&lt;/h4&gt;&lt;p&gt;Финальные решения по контенту согласует CEO. Каждый пост, статья, email-рассылка, реклама — приходят CEO в личку как Decision-карточка с тремя кнопками: одобрить, отклонить, процитируй правки.&lt;p&gt;Случаи, когда CEO отвергал, — реальные. Decision-карточка «WebPhone — пост с видео» отклонена, потому что видео было плохого качества. Карточка «email-дайджест середины апреля» отменена вечером того же дня, потому что повестка изменилась. Это не редкость, это норма.&lt;p&gt;Логика простая: ИИ предлагает, человек одобряет. ИИ выполняет, человек контролирует. Никаких «полностью автоматических публикаций» — мы пробовали, поняли что это плохая идея, вернули контур человека.&lt;h4&gt;Что ломалось&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Race condition с дублями публикаций.&lt;/strong&gt; Если CEO нажимал «одобрить» в момент, когда параллельно работал автоматический тик — два процесса делегировали публикацию, посты выходили дважды. Закрыли через CAS-блокировку (атомарный UPDATE статуса).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Капча Дзена при частых публикациях.&lt;/strong&gt; Дзен ставит капчу, если публикуешь чаще чем раз в 5 минут. Увеличили интервал между постами до 300 секунд.&lt;li&gt;&lt;p&gt;&lt;strong&gt;STT-сервер уходил в OOM.&lt;/strong&gt; Сервис распознавания звонков на собственной инфраструктуре. Поставили мониторинг и автоматический рестарт.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Конфликт UTC/МСК&lt;/strong&gt; — описан в Кейсе №10. Закрыт фундаментально, два этапа фикса.&lt;/ul&gt;&lt;p&gt;Все эти инциденты разбирались публично в том же Telegram-чате. История ошибок и решений хранится в шине событий — мы используем её как обучающие материалы для самой системы.&lt;h4&gt;Архитектура — для тех, кому интересно&lt;/h4&gt;&lt;p&gt;Стек минимально необходимый. Никакой энтерпрайз-обвязки.&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Оркестратор&lt;/strong&gt; — Claude Code (CLI Anthropic), запускает агентов в нужный момент по cron или по событию&lt;li&gt;&lt;p&gt;&lt;strong&gt;12 субагентов&lt;/strong&gt; — каждый под свой канал/задачу: telegram-poster, vk-poster, content-writer, seo-monitor, ads-monitor, sales-analyst, chatwoot-responder, video-producer, market-analytics, company-report, trigger-engine, director&lt;li&gt;&lt;p&gt;&lt;strong&gt;Шина событий&lt;/strong&gt; — SQLite с одной таблицей на всю активность. 90 дней истории, 2900+ событий&lt;li&gt;&lt;p&gt;&lt;strong&gt;GitHub Issues&lt;/strong&gt; — канбан задач, читается агентами&lt;li&gt;&lt;p&gt;&lt;strong&gt;Память&lt;/strong&gt; — markdown-файлы в репозитории + индексация в Qdrant. На сегодня в памяти 441 факт, индексируется ежедневно в 6:00&lt;li&gt;&lt;p&gt;&lt;strong&gt;Интеграции&lt;/strong&gt; — Telegram Bot API, NotiSend (email), VK API, Instagram (instagrapi), Дзен (через Playwright), Yandex.Direct API, MySQL CRM (read-only через SELECT-юзера, write — через ограниченного publisher), Asterisk + STT, и инструмент агрегации сообщений из мессенджеров клиентов (web-chat.org)&lt;/ul&gt;&lt;p&gt;Один сервер. Один человек поддерживает. Никаких монорепо, очередей сообщений, Kubernetes и прочей классики. Скриптовая обвязка вокруг LLM с правильными промптами и доступом к нужным API.&lt;h4&gt;Как тот же подход применить в другом бизнесе&lt;/h4&gt;&lt;p&gt;Всё, что описано выше, мы построили для своей компании. Но та же архитектура применима к любому бизнесу, где есть менеджеры, клиенты, CRM, звонки и реклама. Это особенно показательно для агентств недвижимости — там каждый из перечисленных кейсов решает реальную и больную задачу.&lt;p&gt;&lt;strong&gt;Утренний дайджест руководителю.&lt;/strong&gt; В 9:00 в чат руководителя падает сводка: сколько объектов добавлено вчера и какие из них качественные, сколько звонков клиентам сделано, какие сделки активны и что застряло, какие сотрудники в норме, какие нарушают регламент.&lt;p&gt;&lt;strong&gt;Анализ всех звонков сотрудников клиентам.&lt;/strong&gt; Самое больное место большинства компаний — никто не слушает реальные звонки. РОПа на это не хватает физически: 10 сотрудников × 30 звонков в день = 300 разговоров. Послушать всё невозможно. ИИ не устаёт. Слушает все 300, ставит вердикт по каждому, цитирует факапы дословно, отслеживает повторяющиеся ошибки.&lt;p&gt;&lt;strong&gt;Триггерные письма клиентам с контекстом.&lt;/strong&gt; Клиент сказал в звонке: «нужна квартира до конца года, до 15 млн, для родителей в Бутово». Сотрудник завёл лид и забыл на 4 дня. ИИ читает звонок, видит цели и дедлайн, через 3 дня сам пишет клиенту с упоминанием его конкретных болей. Без участия сотрудника. Клиент чувствует внимание.&lt;p&gt;&lt;strong&gt;Аудит расходов на платное продвижение.&lt;/strong&gt; Компания тратит на платное продвижение десятки тысяч в месяц. Никто не считает по каждому объявлению ROI. ИИ-аудитор каждое утро говорит руководителю: «вот эти 50 объявлений за 3 месяца дали 0 звонков. На рекламу потрачено 47 800 ₽. Отключаем?» Прямой возврат денег.&lt;p&gt;&lt;strong&gt;Контент для блога — из обсуждений в чате.&lt;/strong&gt; Клиент в чате жалуется руководителю на сложность ситуации. ИИ слушает чат, понимает что это типичная боль рынка, и сам предлагает: «делаю гайд». За час — статья в блоге, анонс в Telegram-канале. Без копирайтера.&lt;p&gt;&lt;strong&gt;Продуктовая аналитика клиента в один запрос.&lt;/strong&gt; Сотрудник пишет в общий чат: «посмотри что делал клиент N за последний месяц». ИИ читает CRM, видит активность, сравнивает с похожими профилями и возвращает вывод за минуту. Сотрудник не идёт в техподдержку, не ждёт выгрузку — получает готовый ответ.&lt;p&gt;Любой из этих сценариев строится по одной и той же схеме: подцепиться к данным CRM, прописать правила/триггеры, дать ИИ доступ к нужным API. Разовая работа. Дальше работает само.&lt;h4&gt;Главный вывод&lt;/h4&gt;&lt;p&gt;Сейчас принято говорить «ИИ заменит работников». Это не про нас. ИИ не заменил у нас работников — он заменил &lt;strong&gt;ту работу, которую раньше никто не делал&lt;/strong&gt;: круглосуточный мониторинг, ежедневный детальный анализ, персональные касания каждому клиенту, ROI-аудит каждого рекламного канала, перекрёстная проверка действий менеджеров.&lt;p&gt;На бумаге всё это должны были делать люди. На практике — никогда не делалось, потому что физически невозможно. ИИ заполнил вакуум.&lt;p&gt;Если у вас 5 сотрудников — вакуум закроет один человек на полставки. Если 50 — нужен отдел из 5 человек. Если ИИ — разовая настройка и дальше работает само.&lt;h4&gt;FAQ&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Сколько времени заняла настройка стека?&lt;/strong&gt;&lt;p&gt;Через две недели после старта стек уже работал в автоматическом режиме — публиковал, считал, отчитывался. Дальше — постоянная тонкая донастройка под новые задачи и обратную связь команды. Это нормальный режим: не «сделали и забыли», а постепенно дорабатываем под бизнес. Сейчас вся обвязка работает автономно, вмешательство нужно только когда меняется бизнес-процесс.&lt;p&gt;&lt;strong&gt;Сколько стоит инфраструктура?&lt;/strong&gt;&lt;p&gt;Один сервер (&lt;img class=&#34;formula inline&#34; source=&#34;40/мес) + LLM-токены (~&#34; alt=&#34;40/мес) + LLM-токены (~&#34; src=https://habrastorage.org/getpro/habr/formulas/3/35/350/350bc6297293f7430492c25f439241b2.svg width=224 height=16 data-width=28.927 data-height=2.262 data-vertical-align=-0.566 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/getpro/habr/formulas/3/35/350/350bc6297293f7430492c25f439241b2.svg 780w,&#xA;       https://habrastorage.org/getpro/habr/formulas/3/35/350/350bc6297293f7430492c25f439241b2.svg 781w&#34; loading=lazy decode=async&gt;200/мес при текущей нагрузке) + сторонние API (STT, NotiSend, VK Ads — по тарифам провайдеров). Итого порядка 25–30 тыс. ₽/мес. Замена отделу из 5–7 человек.&lt;p&gt;&lt;strong&gt;Можно ли это повторить?&lt;/strong&gt;&lt;p&gt;Да. Ключевые компоненты — Claude Code (или аналог), SQLite для шины событий, доступ к API нужных сервисов. Промпты для агентов придётся писать самостоятельно под свои бизнес-процессы — это самая трудоёмкая часть.&lt;p&gt;&lt;strong&gt;Что делать, если ИИ ошибся?&lt;/strong&gt;&lt;p&gt;Разобрать публично в том же чате, обновить промпт, добавить факт в память. Каждая ошибка превращается в урок. У нас в библиотеке памяти 441 факт — почти все родились из конкретных уточнений в чате.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>SmartAgent</author>
      <guid>https://habr.com/ru/articles/1030132/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030132</guid>
      <pubDate>Thu, 30 Apr 2026 12:28:12 +0000</pubDate>
    </item>
    <item>
      <title>GEO-продвижение в нейросетях: Bing на SEO Week 2026 анонсировал очень полезные изменения в Bing Webmaster</title>
      <link>https://habr.com/ru/articles/1030126/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030126</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Bing Webmaster Tools постепенно прокачивает отчет AI Performance. На SEO Week в Нью-Йорке Кришна Мадхаван показал новые функции, часть которых Microsoft уже начала выкатывать. &lt;p&gt;&lt;br&gt;Bing — слишком крупный игрок, чтобы отмахиваться от его продуктовой логики. По официальным данным Microsoft, Bing имеет около 155 млн среднемесячных пользователей в ЕС. Глобальные оценки с учетом интеграций в Windows и AI-инструменты показывают аудиторию на уровне 0,9-1 млрд. пользователей в месяц. . По данным Яндекса, озвученным на конференции «День Поиска 2026», на сервис приходится 70% поисковых запросов в России, поиском ежемесячно пользуются более 110 млн уникальных пользователей, а быстрые ответы Алисы AI в Поиске получают 46,5 млн пользователей в месяц. &lt;p&gt;Вывод очевиден: Bing достаточно велик, чтобы его подход к GEO и AI-выдаче внимательно разбирать, а Яндексу стоило бы быстрее приходить к такой же прозрачной аналитике для вебмастеров. &lt;p&gt;На самих слайдах интересно не то, что Bing сказал слово &lt;strong&gt;GEO&lt;/strong&gt; (и они признали вслед за Google). Интересно, в какой форме он это показывает. Там уже виден каркас будущей аналитики: рекомендации, темы, намерения, доля цитирования. Ровно вокруг этих сущностей и будет строиться нормальная работа с присутствием в AI-ответах.&lt;p&gt;Эта логика уже проходила в материалах &lt;strong&gt;«&lt;/strong&gt;&lt;a href=https://habr.com/ru/articles/967690/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Трафик из нейросетей: как попасть в выдачу ИИ и превратить нейроответы в новый канал продвижения&lt;/a&gt;&lt;strong&gt;»&lt;/strong&gt; и &lt;strong&gt;«&lt;/strong&gt;&lt;a href=https://habr.com/ru/articles/929448/ rel=&#34;noopener noreferrer nofollow&#34;&gt;SEO под нейросети в 2025: руководство по GEO/AEO для получения трафика из нейроответов&lt;/a&gt;&lt;strong&gt;»&lt;/strong&gt;: генеративные системы выбирают источники по структуре, полноте закрытия интента и пригодности страницы к извлечению фрагментов, а не по одному точному ключу.&lt;h3&gt;Слайд 1. GEO-focused Recommendations — рекомендации для видимости в генеративных ответах&lt;/h3&gt;&lt;p&gt;На первом слайде Bing показывает четыре блока:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Content structure &amp;amp; crawlability&lt;/strong&gt; — структура контента и доступность для обхода;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Indexing &amp;amp; canonicalization signals&lt;/strong&gt; — индексация и сигналы канонической страницы;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured data adoption &amp;amp; modernization&lt;/strong&gt; — внедрение и актуализация структурированных данных;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Structured data quality &amp;amp; validity&lt;/strong&gt; — качество и корректность структурированных данных.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/1b7/8f0/4bc/1b78f04bc0403ccba41c1fce194cc276.jpeg width=960 height=1280 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/1b7/8f0/4bc/1b78f04bc0403ccba41c1fce194cc276.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/1b7/8f0/4bc/1b78f04bc0403ccba41c1fce194cc276.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Это важный момент. Bing фактически показывает: путь в AI-выдачу начинается с чистой поисковой базы. Сайт должен быть нормально обходим, страницы должны быть однозначно определены как канонические, а сущности — размечены и не конфликтовать между собой.&lt;p&gt;Это хорошо совпадает с тем, что уже разбиралось раньше: генеративная система охотнее берет страницы, где легко вытащить текстовый фрагмент и связать его с понятной сущностью сайта, бренда, услуги, товара или автора.&lt;h4&gt;Что здесь подтверждает Bing&lt;/h4&gt;&lt;p&gt;Во-первых, техническая база сайта напрямую влияет на шансы попадания в AI-ответы.&lt;br&gt;Во-вторых, структурированные данные становятся рабочим сигналом для генеративного поиска.&lt;br&gt;В-третьих, структура и наполненность самой страницы важна так же, как и ее релевантность теме.&lt;h4&gt;Что делать&lt;/h4&gt;&lt;p&gt;Нужен отдельный мини-аудит под этот блок.&lt;p&gt;Сначала техчасть:&lt;ul&gt;&lt;li&gt;&lt;p&gt;проверить, какие страницы реально индексируются, а какие висят в серой зоне;&lt;li&gt;&lt;p&gt;убрать дубли и конфликтующие канонические адреса;&lt;li&gt;&lt;p&gt;убрать цепочки переадресации и страницы, которые размывают сигналы;&lt;li&gt;&lt;p&gt;проверить доступность ключевых материалов для обхода.&lt;/ul&gt;&lt;p&gt;Потом разметка:&lt;ul&gt;&lt;li&gt;&lt;p&gt;разметить организацию, автора, статью, товар, услугу, FAQ — где это уместно;&lt;li&gt;&lt;p&gt;проверить валидность разметки;&lt;li&gt;&lt;p&gt;убедиться, что разметка не противоречит тексту страницы и данным на внешних площадках.&lt;/ul&gt;&lt;p&gt;Потом структура:&lt;ul&gt;&lt;li&gt;&lt;p&gt;один блок — одна мысль;&lt;li&gt;&lt;p&gt;один раздел — одна подтема;&lt;li&gt;&lt;p&gt;ответ на основной вопрос — в начале раздела, а не после длинного вступления;&lt;li&gt;&lt;p&gt;заголовки должны отражать смысл, а не быть контейнерами под ключевые слова.&lt;/ul&gt;&lt;p&gt;Здесь Bing не открыл ничего нового. Он просто показал, что GEO опирается на SEO-базу. И это правильный ход.&lt;h3&gt;Слайд 2. Grounding Query – Topics — темы запросов-оснований&lt;/h3&gt;&lt;p&gt;На втором слайде Bing показывает, что похожие запросы (промпты) он объединяет в общую тему.&lt;p&gt;Это, пожалуй, один из самых сильных слайдов во всей подборке. Потому что здесь Bing формализует то, о чем я давно сделал выводы и на чем построил свой GEO-сервис “Тунец” (&lt;a href=https://habr.com/ru/articles/979268/ rel=&#34;noopener noreferrer nofollow&#34;&gt;тут писал о нем подробно&lt;/a&gt;), когда внедрял там проверку промптов через вордстат и отказался от генерации тысяч промптов для анализа. Важен смысл, а не точное вхождение. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c8f/ed6/eb3/c8fed6eb30beecebcc435f8875689fc0.jpeg width=960 height=1280 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c8f/ed6/eb3/c8fed6eb30beecebcc435f8875689fc0.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c8f/ed6/eb3/c8fed6eb30beecebcc435f8875689fc0.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h4&gt;Что это означает&lt;/h4&gt;&lt;p&gt;Система смотрит не на одну фразу, а на тематический кластер.&lt;br&gt;Для страницы становится важной не только релевантность ключу, но и способность закрыть тему целиком.&lt;br&gt;Хороший материал начинает работать сразу на несколько близких формулировок.&lt;p&gt;Это хорошо совпадает с тем, как работает генеративный поиск на практике. Если у страницы есть определение, критерии выбора, сравнение подходов, ограничения, типовые ошибки и ясные выводы, она становится полезной для широкого набора схожих запросов. Если страница узко подогнана под одну фразу, потенциал ниже.&lt;h4&gt;Что делать&lt;/h4&gt;&lt;p&gt;Здесь уже нужна перестройка контентной логики.&lt;p&gt;Не стоит планировать материалы по схеме “один запрос — одна статья”. Лучше делать так:&lt;ul&gt;&lt;li&gt;&lt;p&gt;собирать темы, а не отдельные ключи;&lt;li&gt;&lt;p&gt;для каждой темы выделять страницу-опору;&lt;li&gt;&lt;p&gt;внутри страницы закрывать несколько слоев: определение, критерии, сравнение, ошибки, ограничения, частые вопросы;&lt;li&gt;&lt;p&gt;создавать статьи на смежные темы и дистрибутировать их.&lt;/ul&gt;&lt;p&gt;Пример. Если страница посвящена GEO, ей мало дать общее определение. Нужны еще различия между SEO, AEO и GEO, типы площадок, метрики, сценарии мониторинга, подходы под разные платформы, требования к структуре страницы и внешним сигналам. Тогда страница начинает работать на десятки близких формулировок.&lt;p&gt;Для старых материалов это особенно важно. Статьи, которые раньше ранжировались по узкой группе запросов, теперь стоит обновлять так, чтобы они закрывали тему шире и содержали готовые смысловые блоки для извлечения в ответ.&lt;h3&gt;Слайд 3. Grounding Query – Intent — намерение запроса-основания&lt;/h3&gt;&lt;p&gt;На третьем слайде Bing раскладывает запросы по типам намерения пользователя. На экране видны классы вроде:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Learning&lt;/strong&gt; — обучение;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Informational Search&lt;/strong&gt; — информационный поиск;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Utility&lt;/strong&gt; — утилитарный запрос;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Navigational&lt;/strong&gt; — навигационный запрос;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Research&lt;/strong&gt; — исследование;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Comparison&lt;/strong&gt; — сравнение;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Planning&lt;/strong&gt; — планирование;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Conversational&lt;/strong&gt; — разговорный запрос.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/0e1/b6f/6da/0e1b6f6da62c6014a07ec062b8570e7d.jpeg width=1280 height=720 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/0e1/b6f/6da/0e1b6f6da62c6014a07ec062b8570e7d.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/0e1/b6f/6da/0e1b6f6da62c6014a07ec062b8570e7d.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Это уже прямой переход от семантики к пользовательской задаче. И это очень показательно.&lt;p&gt;Одна и та же тема может содержать несколько типов намерения.&lt;br&gt;Системе нужен не просто релевантный текст, а текст, который подходит под конкретный сценарий ответа.&lt;br&gt;Контент без разделения по задачам пользователя становится слабее как источник.&lt;p&gt;В этом месте Bing хорошо подтверждает ту логику, которая уже была подробно разобрана в материалах про AEO, GEO и мультиплатформенность: генеративная система выбирает не “длинную статью по теме”, а фрагмент, который лучше всего решает конкретную задачу пользователя.&lt;h4&gt;Что делать&lt;/h4&gt;&lt;p&gt;Нужно попробовать разбить семантику по намерениям. &lt;p&gt;Для каждой темы стоит отдельно отмечать:&lt;ul&gt;&lt;li&gt;&lt;p&gt;где пользователь хочет понять термин;&lt;li&gt;&lt;p&gt;где сравнивает варианты;&lt;li&gt;&lt;p&gt;где выбирает;&lt;li&gt;&lt;p&gt;где исследует рынок;&lt;li&gt;&lt;p&gt;где ищет конкретный бренд или решение;&lt;li&gt;&lt;p&gt;где хочет пошаговый план.&lt;/ul&gt;&lt;p&gt;Под каждый тип намерения нужен свой формат блока или отдельный материал&lt;p&gt;Для &lt;strong&gt;Learning&lt;/strong&gt; / обучения:&lt;ul&gt;&lt;li&gt;&lt;p&gt;короткое точное определение;&lt;li&gt;&lt;p&gt;границы применимости;&lt;li&gt;&lt;p&gt;отличие от близких понятий.&lt;/ul&gt;&lt;p&gt;Для &lt;strong&gt;Comparison&lt;/strong&gt; / сравнения:&lt;ul&gt;&lt;li&gt;&lt;p&gt;таблица критериев;&lt;li&gt;&lt;p&gt;когда выбирать вариант А;&lt;li&gt;&lt;p&gt;когда выбирать вариант Б;&lt;li&gt;&lt;p&gt;короткий вывод в конце блока.&lt;/ul&gt;&lt;p&gt;Для &lt;strong&gt;Research&lt;/strong&gt; / исследования:&lt;ul&gt;&lt;li&gt;&lt;p&gt;карта возможных решений;&lt;li&gt;&lt;p&gt;плюсы и ограничения;&lt;li&gt;&lt;p&gt;параметры выбора;&lt;li&gt;&lt;p&gt;типичные ошибки;&lt;li&gt;&lt;p&gt;агрегация информации.&lt;/ul&gt;&lt;p&gt;Для &lt;strong&gt;Planning&lt;/strong&gt; / планирования:&lt;ul&gt;&lt;li&gt;&lt;p&gt;шаги;&lt;li&gt;&lt;p&gt;сроки;&lt;li&gt;&lt;p&gt;ресурсы;&lt;li&gt;&lt;p&gt;риски;&lt;li&gt;&lt;p&gt;что проверять на каждом этапе.&lt;/ul&gt;&lt;p&gt;Для &lt;strong&gt;Navigational&lt;/strong&gt; / навигации и локального интента:&lt;ul&gt;&lt;li&gt;&lt;p&gt;четкие данные о компании;&lt;li&gt;&lt;p&gt;услуги;&lt;li&gt;&lt;p&gt;регионы;&lt;li&gt;&lt;p&gt;контакты;&lt;li&gt;&lt;p&gt;единые названия и категории.&lt;/ul&gt;&lt;p&gt;и так далее&lt;h3&gt;Слайд 4. Citation Share — доля цитирования&lt;/h3&gt;&lt;p&gt;Самый сильный слайд — четвертый. Bing показывает &lt;strong&gt;Citation Share&lt;/strong&gt;, то есть долю сайта в общем числе цитирований по конкретному запросу-основанию.&lt;p&gt;Вот здесь начинается GEO-аналитика.&lt;p&gt;Потому что до этого рынок часто жил в логике “попали в ответ / не попали”. Такая модель слишком грубая. Бренд может упоминаться случайно, может стабильно быть среди источников, может забирать значимую часть цитирований. Это три разных состояния, и Bing пытается их различать.&lt;p&gt;Появляется метрика реального участия сайта в AI-ответах.&lt;br&gt;Можно будет смотреть не только факт присутствия, но и долю этого присутствия.&lt;br&gt;GEO начинает получать свою нормальную отчетную единицу — как у SEO позиции, показы и переходы. Не абсолютно прозрачно если хочется увидеть конверсию GEO-работы в деньги, но уже очень интересно. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3f9/62b/59d/3f962b59d08913afcce360ed5cfb1948.jpeg width=1280 height=720 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3f9/62b/59d/3f962b59d08913afcce360ed5cfb1948.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3f9/62b/59d/3f962b59d08913afcce360ed5cfb1948.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h4&gt;Что делать&lt;/h4&gt;&lt;p&gt;Во-первых, подождать когда это всё раскатают для всех. Далее собирать собственную матрицу мониторинга. Даже если инструмент еще не дает полный отчет из коробки, сама логика уже понятна.&lt;p&gt;По каждой группе запросов нужно отслеживать:&lt;ul&gt;&lt;li&gt;&lt;p&gt;упоминается ли бренд;&lt;li&gt;&lt;p&gt;цитируется ли домен;&lt;li&gt;&lt;p&gt;какая страница выступает источником;&lt;li&gt;&lt;p&gt;какие конкуренты и какими страницами и материалами забирают большую часть цитирований;&lt;li&gt;&lt;p&gt;повторяется ли результат на близких формулировках;&lt;li&gt;&lt;p&gt;в каком контексте происходит упоминание.&lt;/ul&gt;&lt;p&gt;Во-вторых, отделять долю цитирования от доли трафика. Это разные вещи.&lt;br&gt;Трафик может пока не расти, а присутствие в AI-ответах уже усиливается. Бывает и обратная ситуация.&lt;h3&gt;Что из этого следует для Яндекса&lt;/h3&gt;&lt;p&gt;Вот этого всего как раз сильно не хватает Яндексу. У Яндекса уже есть нейроответы, есть влияние на рынок, есть собственная логика отбора источников. Но вебмастерам почти не дают нормального слоя аналитики под это поведение. А без него рынок остается в режиме догадок, ручных проверок и отдельных наблюдений.&lt;p&gt;Про Google вообще молчу. У них почти ничего нет по GEO в вебмастере)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>Ja-gagarin</author>
      <guid>https://habr.com/ru/articles/1030126/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030126</guid>
      <pubDate>Thu, 30 Apr 2026 12:17:48 +0000</pubDate>
    </item>
    <item>
      <title>Как действительно отдохнуть на майских</title>
      <link>https://habr.com/ru/companies/avito/articles/1028222/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1028222</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Привет, читатели! На связи &lt;strong&gt;Лера Плошкина&lt;/strong&gt;, технический писатель в &lt;a href=https://clc.to/LV3FBg&gt;&lt;strong&gt;Авито&lt;/strong&gt;&lt;/a&gt;. Майские праздники — идеальное время для перезагрузки. Вместо обзора книги в этот раз предлагаю вместе изучить научные стратегии отдыха, основанные на свежих исследованиях когнитивных наук, чтобы вы не просто провалялись все праздники, а действительно восстановились.&lt;p&gt;Давайте начнём с того, что разберёмся, что же такое хороший отдых. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/a9e/a5f/747/a9ea5f74795794e62e8ace98cb9bcae0.jpg width=1560 height=880 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/a9e/a5f/747/a9ea5f74795794e62e8ace98cb9bcae0.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/a9e/a5f/747/a9ea5f74795794e62e8ace98cb9bcae0.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h3&gt;Что такое отдых по науке&lt;/h3&gt;&lt;p&gt;Когда мы говорим «отдохнуть», обычно представляем паузу: выключить ноутбук, лечь на диван, уехать из города. Но для мозга отдых — это не столько отсутствие задач, сколько &lt;strong&gt;переключение режимов работы&lt;/strong&gt;, при котором две вещи происходят параллельно: (1) организм выходит из состояния повышенной мобилизации, и (2) когнитивные системы перестают жить в узком «режиме выполнения» и получают шанс восстановиться.&lt;p&gt;В когнитивных науках и психологии труда это хорошо сочетается с идеей, что после нагрузки нам нужно восстановление, иначе эффект нагрузки «не обнуляется», а копится. &lt;p&gt;Согласно модели восстановления усилий (&lt;em&gt;Effort–Recovery model&lt;/em&gt;) (&lt;a href=&#34;https://www.scirp.org/reference/referencespapers?referenceid=1813198&#34;&gt;Meijman &amp;amp; Mulder, 1998&lt;/a&gt;), после периодов напряжения восстановление нужно именно для того, чтобы &lt;strong&gt;физиологические и психологические реакции вернулись к базовому уровню&lt;/strong&gt;, а не оставались «приподнятыми» и накапливали усталость.&lt;a href=&#34;https://www.scirp.org/reference/referencespapers?referenceid=1813198&amp;amp;utm_source=chatgpt.com&#34;&gt; &lt;/a&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e07/220/4d7/e072204d7ad687f141c56f3fb1d940ff.png alt=&#34;Тут еще больше контента&#34; title=&#34;Тут еще больше контента&#34; width=1320 height=300 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e07/220/4d7/e072204d7ad687f141c56f3fb1d940ff.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e07/220/4d7/e072204d7ad687f141c56f3fb1d940ff.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;a href=https://clc.to/Tr2fwQ&gt;Тут еще больше контента&lt;/a&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Мне нравится объяснение «правильнога отдыха» через &lt;strong&gt;две координатные оси&lt;/strong&gt;. Один и тот же «вечер отдыха» может хорошо сработать по одной оси и провалиться по другой — и тогда вы просыпаетесь с ощущением, что вроде ничего не делали, а легче не стало.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f3a/c3a/fb3/f3ac3afb3cc706d2630daba8ccecf3ff.JPG width=3716 height=2463 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f3a/c3a/fb3/f3ac3afb3cc706d2630daba8ccecf3ff.JPG 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f3a/c3a/fb3/f3ac3afb3cc706d2630daba8ccecf3ff.JPG 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h4&gt;Ось x. Физиологическое «снижение оборотов»: телу нужно выйти из режима угрозы&lt;/h4&gt;&lt;p&gt;Первая ось — &lt;strong&gt;насколько отдых снижает уровень возбуждения организма&lt;/strong&gt;. Это про автономную нервную систему: когда вы в стрессе, тело чаще работает в режиме мобилизации, а восстановление требует включения процессов расслабления.&lt;p&gt;Если отдых не даёт телу «опустить обороты», мозгу труднее восстановить внимание и эмоциональную регуляцию. Это ровно тот случай, когда вы «отдыхаете», но при этом постоянно дёргаетесь на уведомления, проверяете ленту, спорите в комментариях или в голове прокручиваете рабочие диалоги — внешне пауза есть, а внутри система всё ещё на высокой мощности.&lt;p&gt;Хорошая новость: физиологический «downshift» можно тренировать относительно простыми методами. Например, &lt;strong&gt;медленное, спокойное дыхание&lt;/strong&gt;. Не медитация, а буквально несколько минут, когда вы дышите чуть глубже и медленнее обычного. Согласно крупному обзору исследований (&lt;em&gt;Effects of voluntary slow breathing on heart rate and heart rate variability&lt;/em&gt;, 2022), осмысленное дыхание помогает активировать ту часть нервной системы, которая отвечает за расслабление и восстановление. Проще говоря, организм перестаёт быть в режиме тревоги и постепенно сбрасывает напряжение.&lt;p&gt;Именно поэтому иногда прогулка, тёплый душ, растяжка или несколько минут тишины работают лучше, чем «отдохнуть, полистав ленту». Тело понимает такие сигналы очень буквально: если нет резких стимулов, нет спешки и нет ощущения угрозы — можно выключить внутреннюю сирену.&lt;p&gt;Если отдых не даёт этого эффекта, мозг продолжает работать так, будто вы всё ещё на смене. Вы вроде бы лежите на диване, но внутри всё напряжено, мысли скачут, внимание дёргается. В такие моменты дело не в том, что вы «не умеете отдыхать», а в том, что &lt;strong&gt;отдых не снижает нагрузку на нервную систему&lt;/strong&gt;.&lt;h4&gt;Ось y. Когнитивное переключение режима: мозгу нужно выйти из режима контроля в восстановление и переработку&lt;/h4&gt;&lt;p&gt;Вторая ось — &lt;strong&gt;насколько отдых меняет тип умственной работы&lt;/strong&gt;. &lt;p&gt;У мозга есть режимы, которые условно можно назвать «делаю задачу» и «перевариваю опыт». Когда вы постоянно в первом режиме, вы живёте на узком фокусе: держите контекст, подавляете отвлечения, принимаете решения, контролируете эмоции, мониторите риски. Это дорого.&lt;p&gt;Правильный отдых по второй оси — это когда мозг получает возможность &lt;strong&gt;разорвать сцепку с рабочими мыслями&lt;/strong&gt; и перейти в более свободное, внутренне-ориентированное мышление. В психологии труда это близко к понятию &lt;em&gt;psychological detachment&lt;/em&gt; — психологического отделения от работы. Согласно статье о шкале опыта восстановления (&lt;em&gt;The Recovery Experience Questionnaire: Development and Validation…&lt;/em&gt;, &lt;a href=https://kops.uni-konstanz.de/bitstreams/98ac710b-0e57-4fb7-a281-e7a2aa0618ef/download&gt;Sonnentag &amp;amp; Fritz, 2007&lt;/a&gt;), восстановление во многом описывается через четыре переживания: &lt;ol&gt;&lt;li&gt;&lt;p&gt;психологическое отделение;&lt;li&gt;&lt;p&gt;расслабление;&lt;li&gt;&lt;p&gt;ощущение контроля над свободным временем;&lt;li&gt;&lt;p&gt;«мастерство» (деятельность, где вы чувствуете развитие и расширение, но не в формате работы).&lt;/ol&gt;&lt;p&gt;И вот здесь возникает парадокс каникул: можно целый день «отдыхать», но не отделяться. Например, вы не работаете формально, но: читаете рабочие чаты, думаете, как там релиз и это докидывает мозгу угрозы и неопределённость. С точки зрения когнитивных систем это похоже на вечный «фоновой процесс», который не даёт вам закрыть контекст.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/221/132/4e1/2211324e16794fc492f1a0d5e09fe21a.JPG width=2038 height=2236 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/221/132/4e1/2211324e16794fc492f1a0d5e09fe21a.JPG 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/221/132/4e1/2211324e16794fc492f1a0d5e09fe21a.JPG 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h3&gt;Хронический стресс: последствия и предупреждение&lt;/h3&gt;&lt;p&gt;Хронический стресс редко начинается с чего-то большого и драматичного. Чаще он появляется &lt;strong&gt;тихо и постепенно&lt;/strong&gt; — из множества мелких напряжений, которые по отдельности кажутся неопасными.&lt;p&gt;Немного дедлайнов, щепотка неопределённости. Постоянные переключения внимания, сообщения, ожидание ответа, ощущение, что в любой момент может что-то случиться.&lt;p&gt;Проблема в том, что мозг не очень хорошо различает реальные угрозы и абстрактные. Для него сообщение в рабочем чате, тревожная новость или мысль «я что-то упускаю» — это всё сигналы, на которые нужно реагировать. Если такие сигналы появляются снова и снова, система адаптации к стрессу просто не успевает выключаться.&lt;h4&gt;Аллостатическая нагрузка — «износ» от постоянной готовности&lt;/h4&gt;&lt;p&gt;В нейро- и когнитивных науках для этого есть понятие &lt;strong&gt;аллостатической нагрузки&lt;/strong&gt;. Если простыми словами, это «цена», которую платит организм за постоянную адаптацию.&lt;p&gt;Согласно обзору исследований (&lt;em&gt;Allostatic load and the brain&lt;/em&gt;, 2022), стрессовые системы изначально созданы, чтобы помогать нам справляться с трудностями. Но когда они работают слишком долго, без фазы восстановления, это начинает вредить — и телу, и мозгу. Организм всё время живёт в режиме «надо быть начеку», даже если объективной опасности нет.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/7f0/3f3/5ec/7f03f35ec48efb66667b89c19441692b.jpeg alt=https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fnn.4087/MediaObjects/41593\_2015\_Article\_BFnn4087\_Fig1\_HTML.jpg width=1395 height=659 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/7f0/3f3/5ec/7f03f35ec48efb66667b89c19441692b.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/7f0/3f3/5ec/7f03f35ec48efb66667b89c19441692b.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;a href=https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fnn.4087/MediaObjects/41593%5C_2015%5C_Article%5C_BFnn4087%5C_Fig1%5C_HTML.jpg&gt;https://media.springernature.com/full/springer-static/image/art%3A10.1038%2Fnn.4087/MediaObjects/41593\_2015\_Article\_BFnn4087\_Fig1\_HTML.jpg&lt;/a&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Важно, что хронический стресс — это не обязательно сильная тревога, очень часто он выглядит &lt;strong&gt;скучно и буднично&lt;/strong&gt;: постоянная усталость, рассеянность, раздражительность, ощущение, что отдых «не работает».&lt;h3&gt;Какие функции мозга страдают в первую очередь&lt;/h3&gt;&lt;p&gt;Исследования показывают, что при длительном стрессе особенно уязвимы несколько ключевых функций.&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Исполнительные функции&lt;/strong&gt; — всё, что связано с самоконтролем, планированием, удержанием внимания и принятием решений. Именно поэтому в состоянии хронического стресса становится сложнее сосредоточиться, доводить задачи до конца и не прокрастинировать, даже если вы «умеете это делать».&lt;li&gt;&lt;p&gt;&lt;strong&gt;Память и обучение&lt;/strong&gt;. Согласно тому же обзору (2022), стресс влияет на работу систем, связанных с формированием и извлечением воспоминаний. В быту это ощущается как «голова не варит» или «я читаю — и ничего не запоминаю».&lt;li&gt;&lt;p&gt;&lt;strong&gt;Эмоциональная регуляция&lt;/strong&gt;. Мозг становится более реактивным: мы быстрее раздражаемся, сильнее переживаем мелкие неудачи, сложнее успокаиваемся. Не потому что «характер испортился», а потому что ресурсы регуляции уже частично истощены.&lt;/ol&gt;&lt;h3&gt;Как выглядит хронический стресс «изнутри»&lt;/h3&gt;&lt;p&gt;Часто его можно распознать не по тревоге, а по более тонким признакам. Например, когда вы вроде бы отдыхаете, но &lt;strong&gt;не чувствуете облегчения&lt;/strong&gt;. Когда внимание перегревается и всё начинает утомлять. Когда тревога становится «липкой»: не о чём-то конкретном, а просто фоном. Или когда усталость не проходит даже после выходных.&lt;h3&gt;Можно ли это предупредить?&lt;/h3&gt;&lt;p&gt;Хорошая новость в том, что хронический стресс не появляется внезапно — и его можно &lt;strong&gt;заметить и притормозить&lt;/strong&gt; раньше, чем он начнёт серьёзно влиять на качество жизни.&lt;p&gt;Ключевая идея здесь простая: мозгу нужны &lt;strong&gt;регулярные фазы выключения&lt;/strong&gt;, а не только отпуск раз в год. Те самые паузы, где снижается физиологическое напряжение и исчезает необходимость держать всё под контролем.&lt;p&gt;Подготовила для вас чеклист — как найти тот самый work-life balance.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Частота&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Что делать&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Зачем это мозгу&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;5–10 минут медленного дыхания или тишины&lt;td&gt;&lt;p align=left&gt;Снижает физиологическое возбуждение, помогает нервной системе выйти из режима угрозы&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;Короткая прогулка без экрана и аудио&lt;td&gt;&lt;p align=left&gt;Уменьшает внешнюю стимуляцию, даёт телу и вниманию безопасную паузу&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;Любая низкоинтенсивная активность без спешки (растяжка, тёплый душ)&lt;td&gt;&lt;p align=left&gt;Поддерживает переключение автономной нервной системы в режим восстановления&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;Ритуал завершения рабочего дня (записать, что сделано и с чего начать завтра)&lt;td&gt;&lt;p align=left&gt;Помогает мозгу «закрыть контекст» и перестать держать задачи в фоне&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;Минимум один отрезок без входящих сообщений и новостей&lt;td&gt;&lt;p align=left&gt;Снижает когнитивную перегрузку и позволяет вниманию восстановиться&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Несколько раз в неделю&lt;td&gt;&lt;p align=left&gt;Занятие ради процесса, без оценки и результата&lt;td&gt;&lt;p align=left&gt;Переключает мозг из режима контроля в свободное, восстановительное мышление&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Несколько раз в неделю&lt;td&gt;&lt;p align=left&gt;Деятельность, где есть ощущение выбора и контроля&lt;td&gt;&lt;p align=left&gt;Снижает внутреннее напряжение и поддерживает чувство агентности&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;Отключение push-уведомлений от новостей и соцсетей&lt;td&gt;&lt;p align=left&gt;Уменьшает фоновую тревогу и количество стрессовых стимулов&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Каждый день&lt;td&gt;&lt;p align=left&gt;Чтение новостей в заранее выбранное время&lt;td&gt;&lt;p align=left&gt;Снижает эффект постоянной готовности и неопределённости&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Раз в неделю&lt;td&gt;&lt;p align=left&gt;Самопроверка состояния (тело, внимание, ощущение облегчения)&lt;td&gt;&lt;p align=left&gt;Помогает заметить хроническое напряжение до того, как оно накопится&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3&gt;Враг восстановления №1: дробление внимания&lt;/h3&gt;&lt;p&gt;Одна из главных причин, почему отдых не даёт ощущения восстановления, — это &lt;strong&gt;дробление внимания&lt;/strong&gt;. Мы перестаём работать формально, но мозг продолжает быть занят: недочитанными новостями, сообщениями без ответа, мыслями «надо не забыть», ожиданием следующего сигнала.&lt;p&gt;Каждый такой фрагмент кажется мелочью, но для мозга это &lt;strong&gt;открытый контекст&lt;/strong&gt;, который он продолжает удерживать.&lt;p&gt;В когнитивной психологии это явление называют &lt;em&gt;attention residue&lt;/em&gt; — «остаток внимания». Согласно исследованию Софи Леруа (&lt;em&gt;Why is it so hard to do my work?&lt;/em&gt;, 2009), при переключении между задачами часть внимания остаётся на предыдущей задаче, особенно если она была значимой или эмоционально нагруженной. Проще говоря, мозг не закрывает вкладку полностью — он держит её приоткрытой.&lt;p&gt;Из-за этого отдых часто становится фрагментированным. Вы можете лежать, гулять или смотреть сериал, но внимание всё время перескакивает. Внешне пауза есть, а внутри мозг остаётся в режиме мониторинга. Именно поэтому появляется знакомое ощущение: усталость не уходит, мысли скачут, а «отдохнуть» не получается.&lt;p&gt;Социальные сети и новости особенно усиливают этот эффект. Они создают иллюзию отдыха, но на самом деле поддерживают &lt;strong&gt;непрерывную когнитивную стимуляцию&lt;/strong&gt;: микро-переключения, эмоциональные реакции, новые незакрытые мысли. Тело отдыхает, а мозг — нет.&lt;p&gt;Исследования показывают, что для восстановления важно не просто прекратить задачу, а &lt;strong&gt;психологически её завершить&lt;/strong&gt;. Даже простое действие — зафиксировать, что сделано, и с чего вы начнёте потом — снижает количество внимания, которое застревает в фоне. Мозг получает сигнал: «это учтено, можно отпустить».&lt;p&gt;Ключевая мысль здесь простая: мы устаём не только от количества задач, но и от того, что слишком редко бываем &lt;strong&gt;целиком в одном режиме&lt;/strong&gt; — либо в работе, либо в отдыхе. А восстановление начинается там, где внимание перестаёт дробиться и появляется настоящая пауза.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/60b/529/d7f/60b529d7f9762583335e2d98741c1c4a.png alt=&#34;Жми сюда!&#34; title=&#34;Жми сюда!&#34; width=1320 height=300 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/60b/529/d7f/60b529d7f9762583335e2d98741c1c4a.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/60b/529/d7f/60b529d7f9762583335e2d98741c1c4a.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;a href=https://clc.to/MDY_jw&gt;Жми сюда!&lt;/a&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h3&gt;Какой отдых действительно работает: 6 типов восстановления и что каждый из них чинит&lt;/h3&gt;&lt;p&gt;Когда мы говорим «отдых», мы часто ищем универсальный рецепт. Но в реальности мозг устаёт &lt;strong&gt;по-разному&lt;/strong&gt;, и поэтому восстанавливается тоже по-разному. Именно поэтому один и тот же вечер может либо вернуть ощущение ясности, либо оставить чувство пустоты и усталости.&lt;p&gt;Полезно смотреть на отдых как на &lt;strong&gt;инструмент&lt;/strong&gt;, а не как на абстрактное «ничего не делать». Ниже — шесть типов восстановления, которые описываются в когнитивных науках и психологии труда, и ситуации, в которых каждый из них особенно полезен.&lt;h4&gt;1. Сон: восстановление базовых когнитивных функций&lt;/h4&gt;&lt;p&gt;Мозг делает сразу две важные вещи. Во-первых, &lt;strong&gt;консолидирует память&lt;/strong&gt; — закрепляет то, что было выучено и пережито. Во-вторых, снижает общий уровень нейронного «шума», условно занимаясь уборкой и восстановлением ресурсов. Именно поэтому недосып почти сразу бьёт по концентрации, скорости мышления и способности принимать решения.&lt;p&gt;Короткий дневной сон или просто период тихого отдыха без стимулов может работать как &lt;strong&gt;мягкий перезапуск&lt;/strong&gt;, особенно если вы чувствуете перегрузку и «заторможенность». Если вы просыпаетесь уставшими даже после выходных, это часто сигнал не «мало старалась отдохнуть», а &lt;strong&gt;накопленного дефицита восстановления&lt;/strong&gt;, который не закрывается одной паузой.&lt;p&gt;Подробнее о пользе сна я рассказывала &lt;a href=https://habr.com/ru/companies/avito/articles/972338/&gt;в этой статье&lt;/a&gt;. &lt;h4&gt;2. Двигательная активность низкой и средней интенсивности: снижение физиологического напряжения&lt;/h4&gt;&lt;p&gt;Когда тело долго находится в режиме стресса, ему нужно движение — но не любое. Высокоинтенсивные тренировки могут дополнительно нагружать систему, а вот &lt;strong&gt;прогулки, плавание, лёгкий спорт&lt;/strong&gt; часто дают противоположный эффект.&lt;p&gt;Такая активность помогает снизить физиологическое возбуждение и одновременно переключить режим мышления: внимание перестаёт быть узко направленным, а мысли — зацикленными. Именно поэтому после прогулки часто появляется ощущение «стало легче», даже если ничего конкретного не решилось.&lt;p&gt;Об этом классно рассказывается в книге Spark: The Revolutionary New Science of Exercise and the Brain.&lt;h4&gt;3. Природа и «мягкое внимание»: разгрузка системы фокуса&lt;/h4&gt;&lt;p&gt;Контакт с природой — один из самых хорошо исследованных видов восстановления внимания. Согласно теории восстановления внимания (&lt;em&gt;Attention Restoration Theory&lt;/em&gt;, Kaplan &amp;amp; Kaplan, 1989), природные среды активируют так называемое &lt;strong&gt;мягкое внимание&lt;/strong&gt; — состояние, при котором внимание не удерживается усилием, а свободно переключается. Это даёт возможность перегруженным системам фокуса восстановиться.&lt;p&gt;Экспериментальные исследования показывают, что даже короткое пребывание в природной среде улучшает показатели концентрации и снижает субъективную усталость по сравнению с городской средой (Berman et al., 2008). Эффект связан не с «красотой», а с &lt;strong&gt;низкой когнитивной нагрузкой&lt;/strong&gt;: природе не нужно постоянно реагировать.&lt;h4&gt;4. Социальное восстановление: снижение нагрузки саморегуляции&lt;/h4&gt;&lt;p&gt;Общение может как восстанавливать, так и истощать. Разница — в уровне &lt;strong&gt;самоконтроля&lt;/strong&gt;, который оно требует. Согласно исследованиям в психологии труда (Sonnentag &amp;amp; Fritz, 2007), восстановление связано с ощущением психологической безопасности и отсутствием необходимости постоянно регулировать своё поведение. Общение, где не нужно «держать лицо», снижает нагрузку на системы саморегуляции и способствует эмоциональному восстановлению.&lt;p&gt;Это объясняет, почему после одних встреч мы чувствуем тепло и облегчение, а после других — ещё большую усталость.&lt;h4&gt;5. Игровой и творческий режим: восстановление агентности и гибкости мышления&lt;/h4&gt;&lt;p&gt;Творческие и игровые активности восстанавливают не потому, что «отвлекают», а потому что &lt;strong&gt;возвращают ощущение выбора и контроля&lt;/strong&gt;. Исследования показывают, что деятельность, в которой человек действует ради процесса, а не результата, снижает стресс и поддерживает когнитивную гибкость (Deci &amp;amp; Ryan, теория самодетерминации).&lt;p&gt;Если вдруг захотите узнать про это подробнее, то книги &lt;strong&gt;Flow&lt;/strong&gt; М. Чиксентмихайи и &lt;strong&gt;Play&lt;/strong&gt; С. Брауна хорошо дополняют друг друга и помогают понять, почему игровые и творческие состояния восстанавливают мозг. Чиксентмихайи показывает, что глубокая добровольная вовлечённость в процесс снижает внутреннее напряжение и возвращает ощущение смысла и контроля. &lt;p&gt;Браун, опираясь на нейробиологию и клинические наблюдения, объясняет, что игра — это базовая потребность мозга, связанная с креативностью, обучением и эмоциональной устойчивостью. Вместе эти книги помогают пересобрать отношение к отдыху: не как к бездействию, а как к пространству свободы, где возвращается агентность и гибкость мышления. &lt;h4&gt;6. Когнитивная тишина: закрытие открытых контекстов&lt;/h4&gt;&lt;p&gt;Один из самых недооценённых видов отдыха — &lt;strong&gt;периоды без входящего потока информации&lt;/strong&gt;.&lt;p&gt;Исследования переключения внимания показывают, что постоянные стимулы поддерживают эффект &lt;em&gt;attention residue&lt;/em&gt; — остатка внимания, который мешает мозгу полностью переключаться и восстанавливаться (Leroy, 2009). Работы о &lt;em&gt;wakeful rest&lt;/em&gt; (King et al., 2022) показывают, что периоды спокойного бодрствования без новой информации помогают мозгу &lt;strong&gt;консолидировать опыт и снижать когнитивную перегрузку&lt;/strong&gt;.&lt;p&gt;Даже короткие окна тишины — без новостей, чатов и подкастов — дают мозгу шанс перестать мониторить среду.&lt;h3&gt;Как определить свой тип отдыха: чеклист&lt;/h3&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Шаг&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;На что обратить внимание&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Зачем это нужно&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;1. Заметьте состояние&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Где усталость ощущается сильнее: в теле, внимании, эмоциях или ощущении контроля&lt;td&gt;&lt;p align=left&gt;Помогает понять, какая система сейчас перегружена&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;2. Смотрите на эффект&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Стало ли после отдыха спокойнее телу и тише мыслям&lt;td&gt;&lt;p align=left&gt;Позволяет отличить восстановление от просто «проведённого времени»&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;3. Подбирайте формат&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Сон, движение, тишина, общение, творчество — в зависимости от состояния&lt;td&gt;&lt;p align=left&gt;Совпадение типа отдыха с типом усталости ускоряет восстановление&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;4. Разрешайте менять подход&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;То, что работало раньше, может не подходить сейчас&lt;td&gt;&lt;p align=left&gt;Поддерживает адаптацию и снижает давление «надо отдыхать правильно»&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;5. Проверяйте регулярно&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;Есть ли ощущение облегчения, ясности или лёгкости&lt;td&gt;&lt;p align=left&gt;Помогает заметить перегрузку до хронического стресса&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Не обязательно делать всё сразу. Достаточно начать с одного вопроса: что мне сейчас действительно помогает восстановиться? Иногда этого уже достаточно, чтобы отдых стал работать. &lt;h3&gt;Вместо выводов &lt;/h3&gt;&lt;p&gt;Мы живём в мире, где почти всегда есть входящий поток, требования и фоновое напряжение. В таких условиях умение восстанавливаться — не роскошь и не слабость, а навык, который можно постепенно развивать.&lt;p&gt;Пусть эти каникулы будут не про «успеть отдохнуть», а про то, чтобы &lt;strong&gt;заметить себя&lt;/strong&gt;: где вы устали, что вам сейчас нужно и что действительно помогает вам возвращаться в ресурс. Желаю вам спокойного, бережного отдыха — такого, после которого становится чуть тише внутри и немного яснее в голове! &amp;lt;3&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/af8/325/97a/af832597a3f088c86e27e10e8e3c1c1c.png alt=&#34;Кликни здесь и узнаешь&#34; title=&#34;Кликни здесь и узнаешь&#34; width=1320 height=300 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/af8/325/97a/af832597a3f088c86e27e10e8e3c1c1c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/af8/325/97a/af832597a3f088c86e27e10e8e3c1c1c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;a href=https://clc.to/vtMlJg&gt;Кликни здесь и узнаешь&lt;/a&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/avito/articles/1028222/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1028222</guid>
      <pubDate>Thu, 30 Apr 2026 12:15:33 +0000</pubDate>
    </item>
    <item>
      <title>MBA на минималках «Аудит тараканов руководителя»</title>
      <link>https://habr.com/ru/articles/1030122/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030122</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/4c5/0ad/efd/4c50adefd07f5f00d06a0210bda97e28.jpg width=828 height=584 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/4c5/0ad/efd/4c50adefd07f5f00d06a0210bda97e28.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/4c5/0ad/efd/4c50adefd07f5f00d06a0210bda97e28.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;&lt;br&gt;От руководителя, точнее от его решений зависит в бизнесе слишком много.&lt;br&gt;Nokia, Motorola, Volkswagen, Tesla и другие больше компаний были или уничтожены, или потеряли репутация из-за действия именно руководителя.&lt;br&gt;Именно поэтому в MBA и EMBA так важно провести аудит тараканов мышления у руководства бизнеса.&lt;br&gt;Для этого я разработал простой тест из трёх вопросов.&lt;p&gt;1. Как правильно Молдавия или Молдова/ Алматы или Алма-Ата.&lt;p&gt;(это вопрос показывает готов ли руководитель принять изменения или он будет держаться за свои прежние установки. Это будет проявляется и в готовности принять новые технологии.&lt;br&gt;При этом руководитель, который не готов принять желание и ценности других людей, не может построить эффективную самостоятельную команду)&lt;p&gt;2. Почему большой внешний долг США - это не проблема для экономии США.&lt;p&gt;(госдолг США - это лакмусовая бумажка для понимания насколько руководитель понимает как работает экономика.&lt;br&gt;Вероятно, он даже не понимает, что кредит дают тому у кого есть деньги или в кого верят.&lt;br&gt;Ну или он пересмотрел телевизор, и тогда это вообще тяжёлый случай)&lt;p&gt;3. Почему атомная энергетика самая неэффективная и дорогая из всех видов.&lt;p&gt;(этот вопрос проверяет насколько руководитель понимает, что такое стратегия, умеет принимать стратегические решения, просчитывая, не только выгоду здесь и сейчас, но и вероятность больших потерь в будущем, которые сделают решение экономически невыгодным.)&lt;p&gt;Я даю эти вопросы перед обучением руководителей на Индивидуальном Трекинге Руководителей и перед стратегическими сессиями, чтобы понимать с чем необходимо работать, и на что необходимо будет обратить внимание при выработке стратегии бизнеса.&lt;br&gt;Домашнее задание: ответьте сами и попросите Вашу команду ответить на эти вопросы, чтобы проверить себя и свою команду.&lt;p&gt;&lt;strong&gt;#MbaНаМинималках&lt;/strong&gt; &lt;strong&gt;#руководитель&lt;/strong&gt; &lt;strong&gt;#ИндивидуальныйТрекингРуководителя&lt;/strong&gt; &lt;strong&gt;#МихаилБоднарук&lt;/strong&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>Michael_72</author>
      <guid>https://habr.com/ru/articles/1030122/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030122</guid>
      <pubDate>Thu, 30 Apr 2026 12:10:50 +0000</pubDate>
    </item>
    <item>
      <title>Войти в IT после 35: как декрет, волонтёрство и честный разговор с собой полностью изменили мою жизнь</title>
      <link>https://habr.com/ru/articles/1030120/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030120</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;У меня была понятная карьера, звание, опыт и стабильная профессиональная жизнь. Более 13 лет я работала в финансовом управлении МВД Санкт‑Петербурга, руководила отделом и занималась крупными государственными процессами. У меня уже было два высших образования: экономическое и юридическое. А потом, уже в декрете, я стала волонтером у девочки с редким заболеванием, начала общаться с людьми из самых разных сфер, в том числе из IT, и в какой‑то момент поняла: старая версия моей жизни закончилась. Так начался мой путь в разработку, а затем и в продукт.&lt;h3&gt;Мне казалось, что моя жизнь уже давно определена.&lt;/h3&gt;&lt;p&gt;Я окончила Санкт-Петербургский университет МВД России, попала по распределению в финансовое управление и построила там большую карьеру. Более 13 лет работала в системе, руководила отделом, участвовала в систематизации процессов, цифровизации, разработке решений для учета бюджетных средств, штрафов, лицензий, регистрации контрактов через казначейство, сопровождала процессы по 44-ФЗ и работала с крупными контрактами, в том числе по стройке.&lt;p&gt;&lt;em&gt;Это была взрослая, серьезная, уважаемая жизнь.&lt;/em&gt;&lt;p&gt;Но в какой-то момент я поняла: внешне у меня все есть, а внутри уже нет ощущения, что это мой следующий этап.&lt;p&gt;&lt;em&gt;Не потому, что было плохо. А потому, что я внутренне стала больше той жизни, которой тогда жила.&lt;/em&gt;&lt;h3&gt;Декрет не стал паузой. Он стал началом.&lt;/h3&gt;&lt;p&gt;Я активный человек и не умею просто ждать. Мне было важно быть с ребенком, но одновременно важно было не потерять себя.&lt;p&gt;Именно в декрете в моей жизни появилось волонтерство. Я стала волонтером у девочки с  редким заболеванием. В какой‑то момент в группе волонтеров меня попросили взять на себя написание текстов и общение с англоязычными пользователями в Instagram (принадлежит Meta, деятельность которой признана экстремистской и запрещена на территории РФ).&lt;p&gt;&lt;em&gt;Это был важный поворот.&lt;/em&gt;&lt;p&gt;С одной стороны, я снова почувствовала себя нужной: не только как мама, но и как человек, который может организовывать, писать, коммуницировать, брать на себя ответственность.&lt;p&gt;С другой стороны, именно через это сообщество я познакомилась с людьми из самых разных сфер. И среди них были люди из IT. Они иначе говорили о работе, свободе, карьере, развитии, будущем. Через них я впервые увидела, что профессиональную жизнь можно выстраивать совсем по-другому.&lt;p&gt;Если честно, именно тогда внутри меня и начал собираться новый вектор.&lt;h3&gt;В IT я не ушла резко. Я к этому долго шла.&lt;/h3&gt;&lt;p&gt;Со стороны такие истории часто выглядят как внезапный разворот. На деле все было иначе.&lt;p&gt;Сначала был интерес. Потом ролики, интервью, чужие истории на YouTube. Потом попытка понять, что вообще есть в этой сфере и где там могу быть я. Сначала я смотрела в сторону тестирования, потом начала читать про разработку. На старте я даже путала Java и JavaScript - и сейчас мне совсем не стыдно в этом признаться.&lt;p&gt;Потому что так и выглядит настоящий вход в новую жизнь: сначала ты почти ничего не понимаешь, а потом шаг за шагом собираешь новый мир у себя в голове.&lt;p&gt;Наверное, главный момент был в том, что я перестала считать этот интерес случайностью. Я поняла: если меня так тянет в эту сторону, значит, это уже не каприз, а внутренний запрос.&lt;h3&gt;Самым трудным был не код, а новая роль.&lt;/h3&gt;&lt;p&gt;Когда меняешь профессию после 35, сложно не из-за возраста. Сложно из-за того, что у тебя уже есть сильная старая идентичность.&lt;p&gt;Ты была компетентным человеком. Руководила людьми. Несла на себе большие процессы. Уже многое доказала.&lt;p&gt;&lt;em&gt;И вдруг снова становишься новичком.&lt;/em&gt;&lt;p&gt;Вот это и было самым болезненным местом. Не учеба, не собеседования, не код, а внутреннее ощущение, что тебе снова нужно собирать себя заново.&lt;p&gt;Но потом я поняла важную вещь: прошлый опыт никуда не исчезает. Он просто перестает быть готовым ответом и становится фундаментом.&lt;p&gt;&lt;em&gt;Со мной остались:&lt;/em&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;дисциплина;&lt;li&gt;&lt;p&gt;ответственность;&lt;li&gt;&lt;p&gt;системность;&lt;li&gt;&lt;p&gt;умение учиться;&lt;li&gt;&lt;p&gt;способность выдерживать нагрузку;&lt;li&gt;&lt;p&gt;навык общения с разными людьми;&lt;li&gt;&lt;p&gt;спокойствие в сложных ситуациях;&lt;li&gt;&lt;p&gt;аналитическое мышление.&lt;/ul&gt;&lt;p&gt;&lt;em&gt;Именно это и помогло мне не сдаться.&lt;/em&gt;&lt;h3&gt;Учеба была тяжелой. И это важно честно сказать.&lt;/h3&gt;&lt;p&gt;Есть вещи, которые со стороны кажутся проще, чем есть на самом деле.&lt;p&gt;Я училась в дневном кампусе по разработке. Бывали дни, когда после двухчасовой лекции все садились писать код еще на восемь часов. Со стороны казалось, что вокруг меня люди думают быстрее, пишут лучше и вообще гораздо сильнее меня. Особенно когда рядом были ребята после сильных вузов - например, Университета ИТМО или Московского государственного технического университета имени Н. Э. Баумана.&lt;p&gt;&lt;em&gt;В такие моменты очень легко решить, что проблема в тебе.&lt;/em&gt;&lt;p&gt;Но потом, когда начинаешь разговаривать с людьми честно, выясняется, что у них то же самое: они тоже пробуют, стирают, переписывают, не понимают, нервничают, сравнивают себя с другими. У них тоже эмоциональные качели.&lt;p&gt;Это был важный урок: внешняя уверенность почти ничего не говорит о том, как человеку внутри. И если кто-то выглядит сильнее, это не значит, что ему легче.&lt;p&gt;Тогда я поняла еще одну вещь: путь в IT - это не история про &lt;em&gt;“быть самым умным в комнате”. &lt;/em&gt;Это история про темп, дисциплину и готовность продолжать.&lt;h3&gt;Меня поразила не только технология, но и сама культура IT.&lt;/h3&gt;&lt;p&gt;Когда я начала работать в IT-компаниях, меня удивил не только стек и процессы.&lt;p&gt;&lt;em&gt;Меня поразила сама среда.&lt;/em&gt;&lt;p&gt;После госсектора для меня было почти культурным шоком увидеть, что здесь можно разговаривать с директором на “ты”, называть руководителей по имени, спокойно высказывать мнение, спорить по делу, предлагать идеи. В той системе, из которой я пришла, это было почти невозможно представить.&lt;p&gt;Сначала это казалось непривычным. А потом я поняла, насколько мне это близко.&lt;p&gt;&lt;em&gt;Мне нравится среда, где важны не только должность и регламент, а смысл, аргумент и вклад.&lt;/em&gt;&lt;h3&gt;Мой путь в IT оказался шире, чем просто разработка.&lt;/h3&gt;&lt;p&gt;Постепенно я вошла в сферу. Были первые роли, первые реальные задачи, работа с CRM для компании по спецперевозкам, затем компания по заказной разработке, где я была не только техническим специалистом и фуллстек-разработчиком, но и все больше включалась в продуктовую логику.&lt;p&gt;Потом был опыт в Учи.ру. Там я пришла как технический специалист, но получила гораздо больше. Я увидела, как устроен большой продукт, как работает взрослая разработка, как принимаются решения, как строятся команды, как создается ценность для пользователя.&lt;p&gt;&lt;em&gt;И именно там окончательно поняла, что мне очень близко управление продуктом.&lt;/em&gt;&lt;p&gt;Наверное, это и было самым честным продолжением моего пути. Потому что раньше я уже много лет работала с системами, процессами, цифровизацией, изменениями и ответственностью. Просто в одной жизни это происходило в государственной среде, а в другой - в технологической.&lt;p&gt;Сейчас я учусь в Московском физико-техническом институте (МФТИ) на направлении, связанном с управлением IT-продуктом, постоянно участвую в кейс-чемпионатах, прохожу курсы повышения квалификации, много читаю про менеджмент, рост продукта и аналитическое мышление.&lt;p&gt;На данный момент нахожусь в поиске работы как Product Manager, потому что вижу себя именно на стыке людей, процессов, смысла, технологии и развития.&lt;h3&gt;Эта статья появилась не просто так.&lt;/h3&gt;&lt;p&gt;Написать ее меня подтолкнули новые друзья из этой сферы.&lt;p&gt;Но был и еще один момент. После подкаста со мной меня нашли в соцсетях и написали, что посмотрели интервью, увидели мою историю и почувствовали вдохновение. Что мой путь дает им ощущение: жизнь можно менять даже тогда, когда кажется, что уже поздно или слишком страшно.&lt;p&gt;&lt;em&gt;Меня это очень тронуло.&lt;/em&gt;&lt;p&gt;Потому что, наверное, именно ради таких моментов и стоит рассказывать о себе честно: не чтобы казаться сильной, а чтобы показать, что сила часто рождается прямо внутри страха.&lt;p&gt;И еще одна большая радость для меня в том, что за последние пять лет у меня появилось очень много близких по духу людей из IT: разработчиков, продактов, дизайнеров. Это невероятно талантливые люди, многие из которых тоже когда-то пришли сюда из совершенно других сфер.&lt;p&gt;Я настолько вошла в этот мир, что сейчас уже правда живу им: встречи, IT-тусовки, разговоры про продукты, развитие, технологии, идеи. И, наверное, это тоже очень про меня: не просто самой перейти, а еще и вдохновить других.&lt;p&gt;Мне уже удалось привести в IT и нескольких друзей, в том числе человека из медицины. &lt;em&gt;Когда ты находишь свое место, ты начинаешь открывать дорогу и другим.&lt;/em&gt;&lt;h3&gt;Что я хочу сказать этой статьей.&lt;/h3&gt;&lt;p&gt;Наверное, главный смысл этой статьи не в том, что я вошла в IT после 35.&lt;p&gt;&lt;em&gt;Главный смысл в другом: переход в новую сферу - это не только про код, курсы и учебники.&lt;/em&gt;&lt;p&gt;Это еще и про:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;поиск сообщества;&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;разговоры с людьми, которые уже внутри;&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;готовность не понимать с первого раза;&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;дисциплину;&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;ежедневный темп;&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;умение не сбавлять обороты;&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;честность с собой.&lt;/em&gt;&lt;/ul&gt;&lt;p&gt;Если человек идет в IT из другой сферы, ему нужны не только хард-скиллы. Ему нужна среда. Нужны люди, у которых можно спросить. Нужны группы, каналы, сообщества, где неизвестность становится чуть менее страшной.&lt;p&gt;И очень нужна привычка каждый день уделять этому время. Не ждать идеального состояния, а идти. &lt;em&gt;Иногда медленно. Иногда с сомнениями. Но идти.&lt;/em&gt;&lt;h3&gt;Я точно знаю: на этом ничего не заканчивается.&lt;/h3&gt;&lt;p&gt;Мне нравится думать о будущем не как о вершине, а как о длинном живом пути.&lt;p&gt;Сейчас вместе с коллегами по учебе мы уже запланировали следующий этап - обучение в аспирантуре в международном вузе. Мне хочется продолжать расти, углубляться, соединять опыт разработки, продукта, управления и образования.&lt;p&gt;&lt;em&gt;И у меня есть очень ясный образ себя в будущем.&lt;/em&gt;&lt;p&gt;Я вижу себя даже в 75 лет человеком, который преподает в колледжах или университетах, делится опытом, мотивирует людей не бояться менять жизнь, поддерживает тех, кто только начинает, и своим примером показывает, что начинать заново можно не один раз.&lt;p&gt;&lt;em&gt;Мне хочется быть не просто профессионалом. Мне хочется быть человеком, рядом с которым другие тоже разрешают себе перемены.&lt;/em&gt;&lt;h3&gt;Вместо заключения&lt;/h3&gt;&lt;p&gt;&lt;em&gt;Если коротко, моя история не только про IT.&lt;/em&gt;&lt;p&gt;Она про то, как однажды честно признать себе: я хочу другую жизнь.&lt;br&gt;Про то, как декрет может стать не паузой, а началом.&lt;br&gt;Про то, как волонтерство может открыть новые миры.&lt;br&gt;Про то, как люди, которых ты встречаешь случайно, могут изменить траекторию твоей судьбы.&lt;br&gt;Про то, как прошлый опыт не мешает, а помогает.&lt;br&gt;И про то, как важно однажды перестать ждать идеального момента и просто начать.&lt;p&gt;Если моя история что-то и доказывает, то только одно: менять жизнь можно. Даже если сначала страшно. Даже если не по классическому сценарию. Даже если кажется, что все уже давно решено.&lt;p&gt;&lt;em&gt;Ничего не решено, пока ты сам не перестал идти вперед.&lt;/em&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>LianaPogo2025</author>
      <guid>https://habr.com/ru/articles/1030120/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030120</guid>
      <pubDate>Thu, 30 Apr 2026 12:06:02 +0000</pubDate>
    </item>
    <item>
      <title>Рекурсивные типы. Часть 6. Пересвёртка на практике</title>
      <link>https://habr.com/ru/articles/1029528/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029528</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;В этот раз мы рассмотрим, как пересвёртка рекурсивных структур данных помогает в решении задач динамического программирования.&lt;p&gt;Подразумевается, что читатель уже знаком с предыдущими частями обзора, но на всякий случай в первых двух разделах представлены некоторые выдержки оттуда, наиболее важные для данной публикации. Далее мы разберём нюансы реализации пересвёртки — решим проблему переполнения стека и рассмотрим частный случай пересвёртки последовательностей. В конце приводятся примеры решения некоторых задач динамического программирования с использованием полученных ранее функций пересвёртки.&lt;details class=spoiler&gt;&lt;summary&gt;Содержание&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#rec rel=&#34;noopener noreferrer nofollow&#34;&gt;Рекурсия&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#fix rel=&#34;noopener noreferrer nofollow&#34;&gt;Неподвижные точки конструкторов типов&lt;/a&gt;&lt;li&gt;&lt;p&gt;Пересвёртка&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#refold rel=&#34;noopener noreferrer nofollow&#34;&gt;Пересвёртка неподвижной точки&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#stacksafe rel=&#34;noopener noreferrer nofollow&#34;&gt;Защита от переполнения стека&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#refolditer rel=&#34;noopener noreferrer nofollow&#34;&gt;Пересвёртка последовательностей&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;p&gt;Решение задач&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#dynamic rel=&#34;noopener noreferrer nofollow&#34;&gt;Динамическое программирование&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#dotproduct rel=&#34;noopener noreferrer nofollow&#34;&gt;Скалярное произведение сжатых векторов&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#refuelingscount rel=&#34;noopener noreferrer nofollow&#34;&gt;Количество заправок&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#queens rel=&#34;noopener noreferrer nofollow&#34;&gt;Задача о ферзях&lt;/a&gt;&lt;/ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/rss/all/all/#summary rel=&#34;noopener noreferrer nofollow&#34;&gt;Выводы&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Оглавление обзора&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/article/863304/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Рекурсия&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/articles/863324/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Неподвижные точки конструкторов типов&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/articles/863334/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Свободные контейнеры&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/articles/863362/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Схемы рекурсии&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/articles/863376/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Занимательный матан&lt;/a&gt;&lt;li&gt;&lt;p&gt;&lt;a href=https://habr.com/ru/article/1029528/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Пересвёртка на практике&lt;/a&gt;&lt;/ul&gt;&lt;/div&gt;&lt;/details&gt;&lt;h2&gt;Выдержки из предыдущих частей&lt;/h2&gt;&lt;a class=anchor id=rec&gt;&lt;/a&gt;&lt;h3&gt;Рекурсия&lt;/h3&gt;&lt;p&gt;Рекурсивным называется алгоритм, в процессе выполнения которого происходит вызов его самого. Прежде всего, рекурсия — это &lt;em&gt;техника описания&lt;/em&gt; алгоритмов с повторениями. В программировании чаще всего она реализуется через возможность использования идентификаторов, которые будут определены позднее. Например, метод считается полностью определенным, лишь когда завершена его реализация, значит вызовы этого же метода изнутри уже считаются рекурсией. Ссылки на другие неизвестные пока методы также могут приводить к &lt;em&gt;косвенной рекурсии&lt;/em&gt;.&lt;p&gt;Одной из существенных проблем рекурсии является её слабая предсказуемость. Тогда как в обычных циклах условие завершения повторений зафиксировано в одном месте, в случае рекурсии найти и оценить такие условия может быть крайне сложно. Более того, существуют запутанные ситуации, когда даже теоретически невозможно предсказать, завершится ли рекурсивный алгоритм, или зациклится намертво. Поэтому в некоторых языках программирования (F#) по умолчанию запрещены ссылки на ещё не определённые идентификаторы. Это позволяет защититься от нежелательных рекурсивных (циклических) зависимостей, но так или иначе программистам оставляется возможность реализовать рекурсию с помощью дополнительного синтаксиса. Подобную непредсказуемость повторений нельзя устранять полностью, так как она является неотъемлемой характеристикой тьюринг-полноных языков.&lt;p&gt;Поведение алгоритмов изучает такой раздел математики, как &lt;em&gt;лямбда-исчисление&lt;/em&gt;, со всеми своими расширениями. В математике в целом, запрещено ссылаться на то, чего ещё нет. Концепция повторений реализуется в лямбда-исчислении посредством дополнительных кванторов &lt;em&gt;неподвижных точек функций&lt;/em&gt;. Любая функция одного аргумента, принимая на вход свою неподвижную точку, возвращает в точности её же. Такая техника позволяет переписать любую рекурсию через &lt;em&gt;комбинатор неподвижной точки&lt;/em&gt; (Y-комбинатор), сохраняя при этом тьюринг-полноту языка.&lt;p&gt;В программировании мы можем &lt;em&gt;вынести любые повторения&lt;/em&gt; в Y-комбинатор:&lt;pre&gt;&lt;code class=scala&gt;// Y-комбинатор для функций F =&amp;gt; F,  где F = A =&amp;gt; B&#xA;def fix[A, B]: ((A =&amp;gt; B) =&amp;gt; (A =&amp;gt; B)) =&amp;gt; (A =&amp;gt; B) =&#xA;  f =&amp;gt; f(a =&amp;gt; fix(f)(a)) // λ-выражение необходимо для &amp;#34;ленивости&amp;#34;&#xA;&#xA;type Int3 = (Int, Int, Int)&#xA;def fib: (Int3 =&amp;gt; Int) =&amp;gt; (Int3 =&amp;gt; Int) = // функция высшего порядка F =&amp;gt; F&#xA;  self =&amp;gt; (n, a, b) =&amp;gt; n match // аргумент self - &amp;#34;ссылка на себя&amp;#34;&#xA;    case 0 =&amp;gt; a&#xA;    case 1 =&amp;gt; b&#xA;    case _ =&amp;gt; self(n-1, b, a + b)&#xA;&#xA;// неподвижная точка функции высшего порядка - это функция&#xA;val fibonacci: Int =&amp;gt; Int = fix(fib)(_, 0, 1)&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Другая беда рекурсии связана с переполнениями стека. Дело в том, что каждый вызов функции потребляет ценный ресурс — сравнительно небольшую область памяти, выделяемую под стек. Рекурсия обычно связана с глубокой вложенностью вызовов, что часто приводит к исчерпанию стека и знаменитому исключению StackOverflow. Поэтому многие программисты чаще отдают предпочтение циклам взамен рекурсии.&lt;p&gt;Но это проблема не самой концепции рекурсии, а лишь её реализации в современных компьютерах. Есть и такие реализации, которые позволяют сохранить описательные преимущества рекурсии, избегая ловушки переполнения стека. Речь идёт об &lt;em&gt;оптимизации хвостовой рекурсии&lt;/em&gt;, когда «вызов себя» является последним действием в методе. Некоторые компиляторы (Scala) и среды исполнения, обнаружив хвостовой вызов, просто передают управление на начало метода, не потребляя стек. По сути, «под капотом» рекурсия превращается в цикл! Существуют специальные теоремы, доказывающие возможность переписать любую рекурсию в виде цикла и наоборот.&lt;p&gt;Показанный выше Y-комбинатор &lt;code&gt;fix&lt;/code&gt; реализует так называемую &lt;em&gt;неявную&lt;/em&gt; рекурсию. Рекурсивные вызовы инициируются не в теле исходной функции, а уже внутри &lt;code&gt;f&lt;/code&gt;, передаваемой в &lt;code&gt;fix&lt;/code&gt; как аргумент. Заранее неизвестно, сколько раз будет вызвана рекурсия, и сработает ли она вообще. Поэтому комбинатор с такой сигнатурой &lt;em&gt;невозможно привести к стекобезопасному циклу&lt;/em&gt;.&lt;p&gt;Для решения этой задачи необходимо переработать сигнатуру зацикливаемой функции так, чтобы в результате её вычисления &lt;em&gt;сохранялась&lt;/em&gt; полная информация обо всех рекурсивных вызовах, в том числе и многократно вложенных. То есть, тип возвращаемого значения должен быть &lt;strong&gt;рекурсивной структурой данных&lt;/strong&gt;, что соответствует &lt;em&gt;неподвижной точке&lt;/em&gt; некого конструктора типов. Только в этом случае для Y-комбинатора появится возможность стекобезопасной оптимизации.&lt;p&gt;Одной из подходящих рекурсивных структур является встроенный контейнер &lt;code&gt;TailRec[_]&lt;/code&gt; с конструкторами &lt;code&gt;done[A](result: A)&lt;/code&gt; и &lt;code&gt;tailcall[A](rest: =&amp;gt; TailRec[A])&lt;/code&gt;. Он как раз предназначен для обеспечения стекобезопасной рекурсии, и с его помощью наш Y-комбинатор можно переписать так:&lt;pre&gt;&lt;code class=scala&gt;import scala.util.control.TailCalls.*&#xA;&#xA;def fixTail[A, B](f: (A =&amp;gt; TailRec[B]) =&amp;gt; (A =&amp;gt; TailRec[B])): A =&amp;gt; TailRec[B] =&#xA;  a =&amp;gt; tailcall(f(fixTail(f))(a))&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Эта функция нам ещё пригодится позднее, тогда же рассмотрим и некоторые свойства &lt;code&gt;TailRec&lt;/code&gt;. Важно понимать, что на хранение такой структуры данных всё равно будет потребляться память, размер которой пропорционален количеству рекурсивных вызовов. Однако данные будут размещаться уже не в ограниченном стеке, а в динамической памяти (куче).&lt;/p&gt;&lt;a class=anchor id=fix&gt;&lt;/a&gt;&lt;h3&gt;Неподвижные точки конструкторов типов&lt;/h3&gt;&lt;p&gt;Программистам часто приходится работать с рекурсивными структурами данных. Например, такими: «&lt;em&gt;список&lt;/em&gt; — это либо пустой список, либо элемент (голова) и другой &lt;em&gt;список&lt;/em&gt; (хвост)». Здесь видна ссылка на себя, значит, это рекурсивное определение.&lt;p&gt;Основные идеи лямбда-исчисления действуют и на уровне типов, когда роли функций играют &lt;em&gt;конструкторы типов&lt;/em&gt; вроде &lt;code&gt;Option[_]&lt;/code&gt;, преобразующие одни типы в другие. В определении классов обычно допускаются «ссылки на себя», а также косвенная рекурсия. Но больше возможностей и предсказуемости предоставляет техника неподвижных точек конструкторов типов, то есть типов такого вида:&lt;pre&gt;&lt;code class=scala&gt;case class Fix[F[_]](unfix: F[Fix[F]])&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Существуют разные виды неподвижных точек, но прежде всего интересны те из них, которые описывают полярные возможности — конструирование и схлопывание. Так &lt;em&gt;наименьшая неподвижная точка&lt;/em&gt; &lt;img class=&#34;formula inline&#34; source=\mu alt=\mu src=https://habrastorage.org/getpro/habr/formulas/c/c9/c9f/c9faf6ead2cd2c2187bd943488de1d0a.svg width=12 height=16 data-width=1.364 data-height=2.262 data-vertical-align=-0.566 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/getpro/habr/formulas/c/c9/c9f/c9faf6ead2cd2c2187bd943488de1d0a.svg 780w,&#xA;       https://habrastorage.org/getpro/habr/formulas/c/c9/c9f/c9faf6ead2cd2c2187bd943488de1d0a.svg 781w&#34; loading=lazy decode=async&gt; определяется функцией свёртки (fold), позволяющей вычислить некоторое значение на основе всех данных, хранящихся в структуре. В свою очередь, &lt;em&gt;наибольшая неподвижная точка&lt;/em&gt; &lt;img class=&#34;formula inline&#34; source=\mathbb\nu alt=\mathbb\nu src=https://habrastorage.org/getpro/habr/formulas/b/bb/bbc/bbcc2c29e1f42f83c8d459d1a668c120.svg width=12 height=16 data-width=1.199 data-height=2.262 data-vertical-align=-0.566 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/getpro/habr/formulas/b/bb/bbc/bbcc2c29e1f42f83c8d459d1a668c120.svg 780w,&#xA;       https://habrastorage.org/getpro/habr/formulas/b/bb/bbc/bbcc2c29e1f42f83c8d459d1a668c120.svg 781w&#34; loading=lazy decode=async&gt; отвечает за процесс развёртывания (unfold) структуры данных из заданного значения.&lt;p&gt;Наименьшая и наибольшая неподвижные точки функтора &lt;code&gt;F[_]&lt;/code&gt; называются также &lt;em&gt;начальной F-алгеброй&lt;/em&gt; и &lt;em&gt;терминальной F-коалгеброй&lt;/em&gt; соответственно, и для них предоставляются такие функции:&lt;pre&gt;&lt;code class=scala&gt;type   Algebra[F[_]] = [X] =&amp;gt;&amp;gt; F[X] =&amp;gt;   X&#xA;type Coalgebra[F[_]] = [X] =&amp;gt;&amp;gt;   X  =&amp;gt; F[X]&#xA;&#xA;def  inμ[F[+_]: Functor]:   Algebra[F][μ[F]] // F[μ[F]] =&amp;gt;   μ[F]&#xA;def out𝛎[F[+_]: Functor]: Coalgebra[F][𝛎[F]] //   𝛎[F]  =&amp;gt; F[𝛎[F]]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Для свёртки необходимо предоставить &lt;em&gt;алгебру функтора&lt;/em&gt;, тогда как развёртка требует &lt;em&gt;коалгебру&lt;/em&gt;:&lt;pre&gt;&lt;code class=scala&gt;def   foldμ[F[_]]: [X] =&amp;gt;   Algebra[F][X] =&amp;gt; μ[F] =&amp;gt; X&#xA;def unfold𝛎[F[_]]: [X] =&amp;gt; Coalgebra[F][X] =&amp;gt; X =&amp;gt; 𝛎[F]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Наибольшая неподвижная точка представляет собой экзистенциальный тип, который здесь мы запишем в кодировке Чёрча как инструкцию по применению к ней функции-продолжения типа &lt;code&gt;Unfolder&lt;/code&gt;:&lt;pre&gt;&lt;code class=scala&gt;infix type ~&amp;gt;[F[_], G[_]] = [X] =&amp;gt; F[X] =&amp;gt; G[X] // просто похоже на естественное преобразование&#xA;&#xA;type  Unfolder[F[_]] = [R] =&amp;gt;&amp;gt; Coalgebra[F] ~&amp;gt; (* =&amp;gt; R) // функция-продолжение&#xA;&#xA;type μ[F[_]] =  Algebra[F] ~&amp;gt; Id // я могу свернуться в R, если дашь F-алгебру для R&#xA;type 𝛎[F[_]] = Unfolder[F] ~&amp;gt; Id // я могу свернуться в R, если подскажешь, как получить R, имея F-коалгебру и экземпляр X&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;На самом деле все реализации неподвижных точек оказываются изоморфными друг другу (со скидкой на бесконечные конструкции). Из пары функций &lt;code&gt;(fold, in)&lt;/code&gt; всегда можно получить &lt;code&gt;(unfold, out)&lt;/code&gt; и обратно. Значит, все неподвижные точки одинаково характеризуются всеми четырьмя функциями. Это можно выразить с помощью классов типов:&lt;pre&gt;&lt;code class=scala&gt;type   Fold = [Fix[_[_]]] =&amp;gt;&amp;gt; [F[_]] =&amp;gt;&amp;gt; [X] =&amp;gt;&amp;gt; Fix[F] =&amp;gt; X  &#xA;type Unfold = [Fix[_[_]]] =&amp;gt;&amp;gt; [F[_]] =&amp;gt;&amp;gt; [X] =&amp;gt;&amp;gt; X =&amp;gt; Fix[F]  &#xA;  &#xA;type   FoldFix = [Fix[_[_]]] =&amp;gt;&amp;gt; [F[_]] =&amp;gt; Functor[F] ?=&amp;gt; [X] =&amp;gt;   Algebra[F][X] =&amp;gt;   Fold[Fix][F][X]  &#xA;type UnfoldFix = [Fix[_[_]]] =&amp;gt;&amp;gt; [F[_]] =&amp;gt; Functor[F] ?=&amp;gt; [X] =&amp;gt; Coalgebra[F][X] =&amp;gt; Unfold[Fix][F][X]&#xA;  &#xA;type  InFix[Fix[_[_]]] = [F[_]] =&amp;gt; Functor[F] ?=&amp;gt;   Algebra[F][Fix[F]]  &#xA;type OutFix[Fix[_[_]]] = [F[_]] =&amp;gt; Functor[F] ?=&amp;gt; Coalgebra[F][Fix[F]]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Очевидно, что функции начальной алгебры &lt;code&gt;InFix[Fix]&lt;/code&gt; и терминальной коалгебры &lt;code&gt;OutFix[Fix]&lt;/code&gt; образуют изоморфизм — их композиции являются тождественными морфизмами (возвращают в точности то же, что им передали).&lt;p&gt;Чтобы описать неподвижную точку, достаточно реализовать только одну из этих пар. Другая пара выводится автоматически:&lt;pre&gt;&lt;code class=scala&gt;type Functor[F[_]] = [A, B] =&amp;gt; (A =&amp;gt; B) =&amp;gt; (F[A] =&amp;gt; F[B])  &#xA;def fmap[F[_] : Functor as F] = F&#xA;&#xA;given unfoldFromInFix: [Fix[_[_]]: InFix as in] =&amp;gt; UnfoldFix[Fix] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; [X] =&amp;gt; (coalg: Coalgebra[F][X]) =&amp;gt; (x: X) =&amp;gt;  &#xA;    (in[F] `compose` fmap(unfoldFromInFix(coalg)) `compose` coalg)(x)&#xA;  &#xA;given foldFromOutFix: [Fix[_[_]]: OutFix as out] =&amp;gt; FoldFix[Fix] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; [X] =&amp;gt; (alg: Algebra[F][X]) =&amp;gt; (fixF: Fix[F]) =&amp;gt;  &#xA;    (alg `compose` fmap(foldFromOutFix(alg)) `compose` out[F])(fixF)&#xA;&#xA;given outFromIn: [Fix[_[_]]: {InFix as in, FoldFix as fold}] =&amp;gt; OutFix[Fix] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; fold(fmap(in[F]))  &#xA;  &#xA;given inFromOut[Fix[_[_]]: {OutFix as out, UnfoldFix as unfold}]: InFix[Fix] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; unfold(fmap(out[F]))&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Реализации для наименьшей и наибольшей неподвижных точек&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;pre&gt;&lt;code class=scala&gt;given foldμ: FoldFix[μ] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; [X] =&amp;gt; (alg: Algebra[F][X]) =&amp;gt; (fixf: μ[F]) =&amp;gt;  &#xA;    fixf(alg)  &#xA;  &#xA;given inFixμ: InFix[μ] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; (fμF: F[μ[F]]) =&amp;gt; [X] =&amp;gt; (alg: Algebra[F][X]) =&amp;gt;  &#xA;    alg(fmap(summon[Fold[μ]](alg))(fμF))&#xA;&#xA;&#xA;given unfold𝛎: UnfoldFix[𝛎] =&#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; [A] =&amp;gt; (coalg: Coalgebra[F][A]) =&amp;gt; (x: A) =&amp;gt;  &#xA;    [R] =&amp;gt; (cont: CoalgebraRunner[F][R]) =&amp;gt; cont(coalg)(x)  &#xA;    &#xA;given outFix𝛎: OutFix[𝛎] =&#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt; (𝛎F: 𝛎[F]) =&amp;gt;  &#xA;    𝛎F([X] =&amp;gt; (coalg: Coalgebra[F][X]) =&amp;gt; coalg `andThen` fmap(unfold𝛎(coalg)))&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;&lt;p&gt;Для использования возможностей неподвижных точек больше подойдёт следующий конструктор типов:&lt;pre&gt;&lt;code class=scala&gt;type FixedPoint[Fix[_[_]]] = (  &#xA;  fold:     FoldFix[Fix],  &#xA;  unfold: UnfoldFix[Fix],  &#xA;  in:         InFix[Fix],  &#xA;  out:       OutFix[Fix]  &#xA;)  &#xA;  &#xA;given fixFromParts: [Fix[_[_]]: {FoldFix as fold, InFix as in, UnfoldFix as unfold, OutFix as out}] =&amp;gt; FixedPoint[Fix] = (  &#xA;  fold   = fold,  &#xA;  unfold = unfold,  &#xA;  in     = in,  &#xA;  out    = out  &#xA;)  &#xA;  &#xA;inline def   foldFix[Fix[_[_]] : FixedPoint as fix] = fix.fold  &#xA;inline def unfoldFix[Fix[_[_]] : FixedPoint as fix] = fix.unfold  &#xA;inline def     inFix[Fix[_[_]] : FixedPoint as fix] = fix.in  &#xA;inline def    outFix[Fix[_[_]] : FixedPoint as fix] = fix.out&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Например, тип списка и все основные методы работы с ним можно представить в виде неподвижной точки функтора &lt;img class=&#34;formula inline&#34; source=&#34;F = 1 + A \times L&#34; alt=&#34;F = 1 + A \times L&#34; src=https://habrastorage.org/getpro/habr/formulas/0/0c/0cf/0cfc93b126261f5e34b403700c91c7e0.svg width=112 height=16 data-width=14.612 data-height=2.262 data-vertical-align=-0.566 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/getpro/habr/formulas/0/0c/0cf/0cfc93b126261f5e34b403700c91c7e0.svg 780w,&#xA;       https://habrastorage.org/getpro/habr/formulas/0/0c/0cf/0cfc93b126261f5e34b403700c91c7e0.svg 781w&#34; loading=lazy decode=async&gt;:&lt;pre&gt;&lt;code class=scala&gt;type OptCell = [A] =&amp;gt;&amp;gt; [L] =&amp;gt;&amp;gt; Option[(A, L)]&#xA;given optCellFunctor: [X] =&amp;gt; Functor[OptCell[X]] =  &#xA;  [A, B] =&amp;gt; (f: A =&amp;gt; B) =&amp;gt; _.map((x, a) =&amp;gt; (x, f(a)))&#xA;&#xA;type μList[A] = μ[OptCell[A]] // вместо μ можно подставить любую неподвижную точку!&#xA;&#xA;def nil:                              μList[Nothing] = inFix[μ][OptCell[Nothing]](None)  &#xA;def cons[A](head: A, tail: μList[A]): μList[A]       = inFix[μ][OptCell[A]](Some(head -&amp;gt; tail))  &#xA;def foldList[A] = foldFix[μ][OptCell[A]]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Аналогичным образом описываются деревья произвольной формы.&lt;p&gt;Все возможные неподвижные точки представляются деревьями различной формы. И чаще всего мы имеем дело с неподвижными точками &lt;em&gt;полиномиальных функторов&lt;/em&gt;, также известных как &lt;em&gt;алгебраические типы данных&lt;/em&gt; (ADT). У получаемых таким образом деревьев всего несколько ветвей в узле (список — вырожденный случай дерева). Для других же функторов неподвижные точки либо вообще не существуют, либо сложно найти им какое-то применение.&lt;h2&gt;Пересвёртка&lt;/h2&gt;&lt;a class=anchor id=refold&gt;&lt;/a&gt;&lt;h3&gt;Пересвёртка неподвижной точки&lt;/h3&gt;&lt;p&gt;Среди всех возможностей неподвижных точек рекурсивными являются лишь функции &lt;code&gt;unfoldFromInFix&lt;/code&gt; и &lt;code&gt;foldFromOutFix&lt;/code&gt;. Независимо от того, какую пару функций &lt;code&gt;(fold, in)&lt;/code&gt; или &lt;code&gt;(unfold, out)&lt;/code&gt; мы выбираем для задания свойств неподвижной точки, в любом случае одна из функций дополняющей пары окажется рекурсивной. Именно она и будет отвечать за «пробегание по структуре данных».&lt;p&gt;У любой неподвижной точки рекурсивной будет либо развёртка, либо свёртка. Но для широкого спектра задач оказывается важной только их комбинация, называемая «пересвёрткой» (refold) или гиломорфизмом (hylomorphism):&lt;pre&gt;&lt;code class=scala&gt;type Refold[Fix[_[_]]] = [F[_]] =&amp;gt; Functor[F] ?=&amp;gt;&#xA;  [A, B] =&amp;gt; (Coalgebra[F][A], Algebra[F][B]) =&amp;gt; (A =&amp;gt; B)&#xA;&#xA;def refoldFix[Fix[_[_]]: FixedPoint]: Refold[Fix] =  &#xA;  [F[_]] =&amp;gt; (_: Functor[F]) ?=&amp;gt;&#xA;    [A, B] =&amp;gt; (coalg: Coalgebra[F][A], alg: Algebra[F][B]) =&amp;gt;  &#xA;&#x9;  foldFix(alg) `compose` unfoldFix(coalg) // свёртка после развёртки&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Эта функция будет рекурсивной для любого &lt;code&gt;Fix&lt;/code&gt;.&lt;p&gt;Если подставить (формально) вместо &lt;code&gt;unfoldFix&lt;/code&gt; и &lt;code&gt;foldFix&lt;/code&gt; определения &lt;code&gt;unfoldFromInFix&lt;/code&gt; и &lt;code&gt;foldFromOutFix&lt;/code&gt;, то пересвёртку можно сильно упростить:&lt;/p&gt;&lt;img class=formula source=&#34;\begin{split} refold(\varphi, \psi) = &amp;amp;&amp;amp; fold(\psi) &amp;amp;\circ unfold(\varphi) &amp;amp;&amp;amp; \hspace{5mm} \text{определение} \\ =&amp;amp;&amp;amp; \psi \circ F(fold(\psi)) \circ out &amp;amp;\circ in \circ F(unfold(\varphi)) \circ \varphi &amp;amp;&amp;amp; \hspace{5mm} \text{развернули $fold$ и $unfold$} \\  =&amp;amp;&amp;amp; \psi \circ F(fold(\psi)) &amp;amp;\circ F(unfold(\varphi)) \circ \varphi &amp;amp;&amp;amp; \hspace{5mm} \text{учли, что}\; out \circ in = id \\  =&amp;amp;&amp;amp; \psi \circ F(fold(\psi) &amp;amp;\circ unfold(\varphi)) \circ \varphi &amp;amp;&amp;amp; \hspace{5mm} \text{функтор: }\; F(f) \circ F(g) = F(f \circ g) \\  =&amp;amp;&amp;amp; \psi \circ F(ref&amp;amp;old(\varphi, \psi)) \circ \varphi. &amp;amp;&amp;amp; \hspace{5mm} \text{подставили определение}  \end{split}&#34; alt=&#34;\begin{split} refold(\varphi, \psi) = &amp;amp;&amp;amp; fold(\psi) &amp;amp;\circ unfold(\varphi) &amp;amp;&amp;amp; \hspace{5mm} \text{определение} \\ =&amp;amp;&amp;amp; \psi \circ F(fold(\psi)) \circ out &amp;amp;\circ in \circ F(unfold(\varphi)) \circ \varphi &amp;amp;&amp;amp; \hspace{5mm} \text{развернули $fold$ и $unfold$} \\  =&amp;amp;&amp;amp; \psi \circ F(fold(\psi)) &amp;amp;\circ F(unfold(\varphi)) \circ \varphi &amp;amp;&amp;amp; \hspace{5mm} \text{учли, что}\; out \circ in = id \\  =&amp;amp;&amp;amp; \psi \circ F(fold(\psi) &amp;amp;\circ unfold(\varphi)) \circ \varphi &amp;amp;&amp;amp; \hspace{5mm} \text{функтор: }\; F(f) \circ F(g) = F(f \circ g) \\  =&amp;amp;&amp;amp; \psi \circ F(ref&amp;amp;old(\varphi, \psi)) \circ \varphi. &amp;amp;&amp;amp; \hspace{5mm} \text{подставили определение}  \end{split}&#34; src=https://habrastorage.org/getpro/habr/formulas/b/be/bee/beea2719121a9a24ab9ab621a7449bb7.svg width=744 height=112 data-width=93.765 data-height=14.027 data-vertical-align=-6.448 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/getpro/habr/formulas/b/be/bee/beea2719121a9a24ab9ab621a7449bb7.svg 780w,&#xA;       https://habrastorage.org/getpro/habr/formulas/b/be/bee/beea2719121a9a24ab9ab621a7449bb7.svg 781w&#34; loading=lazy decode=async&gt;&lt;p&gt;Упоминание неподвижной точки вообще выпадает из определения пересвёртки!&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/85b/04b/648/85b04b6489e696732b8930c3cf4c8fc7.png alt=&#34;Неподвижная точка не участвует в пересвёртке, но руководит ею с помощью функтора.&#34; title=&#34;Неподвижная точка не участвует в пересвёртке, но руководит ею с помощью функтора.&#34; width=780 height=440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/85b/04b/648/85b04b6489e696732b8930c3cf4c8fc7.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/85b/04b/648/85b04b6489e696732b8930c3cf4c8fc7.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Неподвижная точка не участвует в пересвёртке, но руководит ею с помощью функтора.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;А всю рекурсию можно спрятать в Y-комбинатор &lt;code&gt;fix&lt;/code&gt;, определённый ранее:&lt;pre&gt;&lt;code class=scala&gt;def hylo[F[_]: Functor, A, B](  // «облегчённая» пересвёртка&#xA;  coalg: Coalgebra[F][A],  &#xA;  alg:     Algebra[F][B]&#xA;): A =&amp;gt; B =  &#xA;  fix(alg `compose` fmap(_) `compose` coalg)&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;На использовании этой мощной функции будут строиться представленные далее решения типовых задач динамического программирования.&lt;/p&gt;&lt;a class=anchor id=stacksafe&gt;&lt;/a&gt;&lt;h3&gt;Защита от переполнения стека&lt;/h3&gt;&lt;p&gt;В приведённой выше реализации гиломорфизма рекурсивные вызовы «спрятаны» внутри &lt;code&gt;fmap&lt;/code&gt;. Они полностью подчинены неизвестному функтору &lt;code&gt;F&lt;/code&gt;, что не позволяет в общем случае развернуть рекурсию в линейный цикл. Впрочем, как было отмечено выше, структуру вызовов можно сохранять в контейнере &lt;code&gt;TailRec&lt;/code&gt;. Нужно изменить шаг рекурсии, чтобы он возвращал значение в этом контейнере, что позволит воспользоваться «безопасным» Y-комбинатором &lt;code&gt;fixTail&lt;/code&gt;.&lt;p&gt;Но всплывает один нюанс — у нас теперь есть два контейнера &lt;code&gt;F[_]&lt;/code&gt; и &lt;code&gt;TailRec[_]&lt;/code&gt;, и чтобы свести концы с концами, нам потребуется возможность «вытащить» &lt;code&gt;F&lt;/code&gt; изнутри &lt;code&gt;TailRec[_]&lt;/code&gt;. Нам нужен &lt;em&gt;дистрибутивный закон&lt;/em&gt; для этих контейнеров:&lt;pre&gt;&lt;code class=scala&gt;type ∘[F[_], G[_]] = [X] =&amp;gt;&amp;gt; F[G[X]]          // композиция контейнеров&#xA;infix type ⇄[F[_], G[_]] = (F ∘ G) ~&amp;gt; (G ∘ F) // дистрибутивный закон&#xA;&#xA;type SwapOut[F[_]] = [G[_]] =&amp;gt;&amp;gt; F ⇄ G        // классы типов&#xA;type SwapIn [F[_]] = [G[_]] =&amp;gt;&amp;gt; G ⇄ F        // мы используем этот&#xA;&#xA;inline def swap[F[_], G[_]](using sw: F ⇄ G) = sw&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Экземпляр &lt;code&gt;SwapIn[TailRec][F]&lt;/code&gt; подсказывает, как именно нужно «пробежать» по структуре &lt;code&gt;F[_]&lt;/code&gt;, чтобы пересобрать её уже внутри &lt;code&gt;TailRec[_]&lt;/code&gt;.&lt;p&gt;В результате «безопасный» гиломорфизм можно записать так:&lt;pre&gt;&lt;code class=scala&gt;// для удобства&#xA;inline def  runTailRec: TailRec ~&amp;gt; Id    = [A]    =&amp;gt; _.result&#xA;inline def fmapTailRec: Functor[TailRec] = [A, B] =&amp;gt; (f: A =&amp;gt; B) =&amp;gt; _.map(f)&#xA;  &#xA;def safeHylo[F[_]: {Functor, SwapIn[TailRec] as swap}, A, B](  &#xA;  coalg: Coalgebra[F][A],  &#xA;  alg:     Algebra[F][B]  &#xA;): A =&amp;gt; B =  &#xA;  fixTail((self: A =&amp;gt; TailRec[B]) =&amp;gt;  &#xA;    fmapTailRec(alg) `compose`  // алгебру «поднимаем в мир TailRec»&#xA;    swap[B]          `compose`  // добавился этот шаг&#xA;    fmap(self)       `compose`  // также как и в hylo&#xA;    coalg                       // также как и в hylo &#xA;  ) `andThen` runTailRec[B]     // в самом конце «распаковываем» TailRec[B]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Такая реализация действительно не боится переполнения стека!&lt;p&gt;В библиотеках функционального программирования обычно используется не такой дистрибутивный закон &lt;code&gt;⇄&lt;/code&gt;, а специальные классы типов для пробрасывания целевого контейнера внутрь (&lt;code&gt;Traverse[F[_]]&lt;/code&gt;) или наружу (&lt;code&gt;Distributive[F[_]]&lt;/code&gt;) &lt;em&gt;любого другого&lt;/em&gt; контейнера. Такое разделение базируется на фундаментальных закономерностях из теории категорий. Для наших целей вполне подошли бы экземпляры &lt;code&gt;Traverse[F]&lt;/code&gt; или &lt;code&gt;Distributive[TailRec]&lt;/code&gt; вместо узкоспециализированного &lt;code&gt;SwapIn[TailRec][F]&lt;/code&gt;.&lt;p&gt;Оказывается, что для контейнера &lt;code&gt;TailRec&lt;/code&gt; невозможно реализовать честный экземпляр &lt;code&gt;Distributive&lt;/code&gt;. Зато реализация &lt;code&gt;Traverse&lt;/code&gt; для полиномиальных конструкторов типов доступна всегда. Scala-библиотека &lt;a href=https://github.com/typelevel/kittens rel=&#34;noopener noreferrer nofollow&#34;&gt;Kittens&lt;/a&gt; из экосистемы Cats даже предоставляет механизмы &lt;em&gt;автоматического вывода&lt;/em&gt; экземпляров &lt;code&gt;Traverse&lt;/code&gt; для таких &lt;code&gt;F[_]&lt;/code&gt;. Таким образом, на практике безопасный гиломорфизм не накладывает на программистов обязательств по «ручной» реализации дистрибутивного закона &lt;code&gt;F ∘ TailRec ~&amp;gt; TailRec ∘ F&lt;/code&gt;.&lt;p&gt;Контейнер &lt;code&gt;TailRec&lt;/code&gt; уже рассматривался в &lt;a href=https://habr.com/ru/articles/863334/#trampolines rel=&#34;noopener noreferrer nofollow&#34;&gt;третьей части&lt;/a&gt; данного обзора, напомню лишь основные моменты. Этот контейнер является минималистичной реализацией паттерна «батут» (trampoline). По сути, он изоморфен свободной монаде, которая, в свою очередь, является неподвижной точкой конструктора типов:&lt;pre&gt;&lt;code class=scala&gt;type FreeBase[F[_]] = [A] =&amp;gt;&amp;gt; A `Either` F  &#xA;type FreeF[Fix[_[_]]] = [F[_]] =&amp;gt;&amp;gt; [A] =&amp;gt;&amp;gt; FixedPoint[Fix] ?=&amp;gt; Fix[FreeBase[F][A]]  &#xA;// FreeF[Fix][F][A] ≅ (Id + F + F∘F + F∘F∘F + …)[A]&#xA;&#xA;type Thunk[A] = () =&amp;gt; A&#xA;type Trampoline[Fix[_[_]]] = FreeF[Fix][Thunk] // Fix[[X] =&amp;gt;&amp;gt; Either[A, () =&amp;gt; X]]&#xA;&#xA;// TailRec[A] ≅ Trampoline[Fix][A] для любой неподвижной точки Fix&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Благодаря «свободной структуре» &lt;code&gt;TailRec&lt;/code&gt; накапливает дерево всех своих преобразований, а &lt;code&gt;Thunk[_]&lt;/code&gt; позволяет делать это «лениво». Но самая хитрая магия скрыта в методе &lt;code&gt;.result&lt;/code&gt; (наш &lt;code&gt;runTailRec&lt;/code&gt;). Дело в том, что в нём реализован механизм &lt;a href=https://habr.com/ru/articles/1012402/#codencity rel=&#34;noopener noreferrer nofollow&#34;&gt;коплотности&lt;/a&gt;, характерный для большинства &lt;a href=https://habr.com/ru/articles/1012402/#fastfree rel=&#34;noopener noreferrer nofollow&#34;&gt;современных свободных монад&lt;/a&gt;. Он позволяет &lt;em&gt;линеаризовать&lt;/em&gt; накопленную иерархию, чтобы вместо ресурсоемкого обхода дерева выполнялся один быстрый проход. В итоге мы получаем константное время на каждый шаг вычисления и полную защиту от переполнения стека.&lt;p&gt;Наш &lt;code&gt;safeHylo&lt;/code&gt; полностью закрывает потребность в стекобезопасной пересвёртке, однако он нечасто находит применение. Дело в том, что на практике мы обычно работаем с ограниченными объёмами данных. Во многих задачах, разворачивая &lt;em&gt;даже бинарное&lt;/em&gt; дерево на тридцать уровней, на последнем мы можем получить больше миллиарда объектов, тогда как его рекурсивный обход задействует всего тридцать фреймов стека, что достаточно безопасно. В частном же случае линейной последовательности (вырожденное дерево) также не понадобится сохранять промежуточные данные в памяти, а рекурсию всегда несложно свести к прямому циклу. Давайте разберём этот случай подробнее.&lt;/p&gt;&lt;a class=anchor id=refolditer&gt;&lt;/a&gt;&lt;h3&gt;Пересвёртка последовательностей&lt;/h3&gt;&lt;p&gt;Когда конструктор типов &lt;code&gt;F[_]&lt;/code&gt; фиксирован, мы всегда знаем, как именно будут осуществляться рекурсивные вызовы, и можем сделать их хвостовыми. Последовательности элементов &lt;code&gt;A&lt;/code&gt; изоморфны неподвижной точке от &lt;code&gt;OptCell[A][_]&lt;/code&gt;. Значит, для её рекурсивной пересвёртки подойдёт такая реализация:&lt;pre&gt;&lt;code class=scala&gt;def refoldRec[A, B, C](  &#xA;  coalgebra: Coalgebra[OptCell[A]][B],  &#xA;  defaultC: C,             // эти два параметра вместе&#xA;  accumulate: (C, A) =&amp;gt; C  // эквивалентны Algebra[OptCell[A]][C]&#xA;): B =&amp;gt; C = (seedB: B) =&amp;gt;  &#xA;  @tailrec  &#xA;  def loop(currentC: C, currentSeed: B): C =  &#xA;    coalgebra(currentSeed) match  &#xA;      case None =&amp;gt; currentC&#xA;      case Some((a, nextSeed)) =&amp;gt; loop(accumulate(currentC, a), nextSeed)  &#xA;  &#xA;  loop(defaultC, seedB)  &#xA;end refoldRec&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Впрочем, для линейных итераций необязательно тянуть в свою кодовую базу подобный «велосипед». В стандартной библиотеке Scala уже есть подходящее решение — &lt;code&gt;Iterator&lt;/code&gt;. С его помощью пересвёртку последовательности можно реализовать следующим образом:&lt;pre&gt;&lt;code class=scala&gt;def refoldIter[A, B, C](  &#xA;  coalgebra: Coalgebra[OptCell[A]][B],  &#xA;  defaultC: C,  &#xA;  accum: (C, A) =&amp;gt; C  &#xA;): B =&amp;gt; C =  &#xA;  (seedB: B) =&amp;gt; Iterator  &#xA;    .unfold(seedB)(coalgebra)  &#xA;    .foldLeft(defaultC)(accum)  &lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Метод &lt;code&gt;unfold&lt;/code&gt; создаёт экземпляр типа &lt;code&gt;Iterator[A]&lt;/code&gt;. Он генерирует новое состояние посредством &lt;code&gt;coalgebra&lt;/code&gt; лишь в тот момент, когда его запрашивает свёртка &lt;code&gt;foldLeft&lt;/code&gt;, которая сразу же потребляет это состояние, не сохраняя его в памяти. В итоге мы практически «из коробки» имеем оптимизированную стекобезопасную пересвёртку последовательности.&lt;p&gt;Метод &lt;code&gt;foldLeft&lt;/code&gt; работает «до конца», что не всегда бывает удобно. Зачастую бывает полезно найти лишь нужный элемент или профильтровать. Для этих целей у &lt;code&gt;Iterator&lt;/code&gt; предусмотрены методы &lt;code&gt;filter&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt; и &lt;code&gt;takeWhile&lt;/code&gt;.&lt;p&gt;Но «ручная» работа с &lt;code&gt;Iterator&lt;/code&gt; может быть опасна, так как это не честный функциональный контейнер, а &lt;em&gt;изменяемый тип данных&lt;/em&gt;. Он как река — нельзя дважды «войти» в один и тот же итератор. Поэтому в целях защиты от возможных ошибок, полезно прятать его от разработчиков в безопасные методы наподобие &lt;code&gt;refold&lt;/code&gt;.&lt;p&gt;Например, механизм &lt;em&gt;быстрого выхода&lt;/em&gt; (short circuit) можно унифицировать с приведённой выше функцией. При этом необходимо изменить сигнатуру аккумулятора: &lt;code&gt;accum: (C, A) =&amp;gt; Either[C, D]&lt;/code&gt;. Семантика такова: либо продолжаем вычисления со следующим &lt;code&gt;C&lt;/code&gt;, либо успешно завершаем их с &lt;code&gt;D&lt;/code&gt;. И эту алгебру достаточно «встроить» в коалгебру:&lt;pre&gt;&lt;code class=scala&gt;type Result[D, C] = D `Either` C // завершила либо алгебра с D, либо коалгебра с C&#xA;&#xA;def upgradeCoalgebra[A, B, C, D](  &#xA;  coalg: Coalgebra[OptCell[A]][B],  &#xA;  alg: (C, A) =&amp;gt; Either[C, D]   // результат ↓     либо продолжение  &#xA;): Coalgebra[OptCell[Result[D, C]]][Result[D, C] `Either` (B, C)] =  &#xA;  &#xA;  def resultWithLeft[R](r: R) = r -&amp;gt; Left(r) // для удобства&#xA;   &#xA;  _.toOption.map { (b, c) =&amp;gt; coalg(b)&#xA;    .fold(resultWithLeft(Right(c))) { (a, nextB) =&amp;gt; alg(c, a)&#xA;&#x9;  .fold( // из-за семантики алгебры левая ветка даёт правую и наоборот&#xA;        nextC  =&amp;gt; Right(nextC) -&amp;gt; Right((nextB, nextC)), // продолжение вычислений&#xA;        finalD =&amp;gt; resultWithLeft(Left(finalD))           // остановка&#xA;      )  &#xA;    }&#xA;  }&#xA;end upgradeCoalgebra&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Тогда «короткая» пересвёртка будет такой:&lt;pre&gt;&lt;code class=scala&gt;def lastAccum[A, C](a: A, c: C) = c&#xA;&#xA;def shortRefold[A, B, C, D](&#xA;  coalg: Coalgebra[OptCell[A]][B],  &#xA;  defaultC: C,  &#xA;  accum: (C, A) =&amp;gt; Either[C, D]  &#xA;): B =&amp;gt; Either[D, C] = seedB =&amp;gt;  &#xA;  val superCoalg = upgradeCoalgebra(coalg, accum)  &#xA;  val rightDefault: Either[D, C] = Right(defaultC)  &#xA;  refoldIter(superCoalg, rightDefault, lastAccum)(Right((seedB, defaultC)))&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Пример использования короткой пересврйтки&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;pre&gt;&lt;code class=scala&gt;// даёт пердыдущее чилсло, большее 0&#xA;val natCoalg: Coalgebra[OptCell[Int]][Int] =&#xA;  n =&amp;gt; Option.when(n &amp;gt; 0)(n, n - 1)&#xA;&#xA;// если нашли ответ, возвращаем строку, иначе - следующее число&#xA;val shortAaccum(goal: Int): (Int, Int) =&amp;gt; Either[Int, String] =&#xA;  (counter, x) =&amp;gt; Either.cond(x == goal, s&amp;#34;нашли на $counter&amp;#34;, counter + 1)&#xA;&#xA;shortRefold(natCoalg, 0, shortAaccum(goal = 999999958))(1000000000) // Left(нашли на 42)&#xA;shortRefold(natCoalg, 0, shortAaccum(goal =        -1))(1000000000) // Right(1000000000)&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В первом случае будет всего сорок две итерации, тогда как во втором — миллиард.&lt;/div&gt;&lt;/details&gt;&lt;p&gt;Если нужно просто развернуть последовательность, которая будет использоваться более одного раза, то вместо итератора удобнее взять коллекцию &lt;code&gt;LazyList&lt;/code&gt;. Её метод &lt;code&gt;unfold&lt;/code&gt; работает аналогично &lt;code&gt;Iterator.unfold&lt;/code&gt;, но после первой пробежки все элементы будут кэшированы, и при повторном использовании они не будут вычисляться заново.&lt;p&gt;Аналогичным образом можно реализовать стратегию раннего выхода и для пересвёрток более сложных структур данных. Необходимым условием для этого является наличие терминального слагаемого в &lt;code&gt;F[_]&lt;/code&gt;. Например, неподвижная точка &lt;code&gt;F[X] = Option[(A, X, X)]&lt;/code&gt; порождает бинарное дерево элементов &lt;code&gt;A&lt;/code&gt; и для его пересвёртки также доступен ранний выход.&lt;h2&gt;Решение задач&lt;/h2&gt;&lt;a class=anchor id=dynamic&gt;&lt;/a&gt;&lt;h3&gt;Динамическое программирование&lt;/h3&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/062/37d/3e9/06237d3e9ba4376aeeccc9f76629f7f1.png alt=&#34;Если бы мы знали, что такое это ваше Динамическое Программирование... Страшно!&#34; title=&#34;Если бы мы знали, что такое это ваше Динамическое Программирование... Страшно!&#34; width=1280 height=720 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/062/37d/3e9/06237d3e9ba4376aeeccc9f76629f7f1.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/062/37d/3e9/06237d3e9ba4376aeeccc9f76629f7f1.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Если бы мы знали, что такое это ваше Динамическое Программирование... Страшно!&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Общепринятого определения динамического программирования не существует. Это просто очередная маркетинговая этикетка, под которой удобно продавать различные медиа-материалы (вроде данной публикации)). Например, в Википедии приводится такая банальная формулировка: «это способ решения сложных задач путём разбиения их на более простые подзадачи». Но такое определение описывает общий метод решения абсолютно любых задач (декомпозиция, «разделяй и властвуй» и т.п.).&lt;p&gt;Здесь же сошлюсь на идею, почерпнутую в хабр-статье &lt;a href=https://habr.com/ru/articles/777618/ rel=&#34;noopener noreferrer nofollow&#34;&gt;О динамическом программировании на пальцах&lt;/a&gt;. Там говорится о «рассмотрении всех возможных путей решения, и выбора лучших среди них». В терминах теории типов получаем такое обобщение:&lt;blockquote&gt;&lt;p&gt;Развёртка рекурсивной структуры промежуточных данных с последующей её свёрткой с поиском/накоплением нужного результата.&lt;/blockquote&gt;&lt;p&gt;Практически каждый алгоритм под этикеткой «Динамического программирования» сводится к &lt;em&gt;пересвёртке неподвижной точки полиномиального функтора&lt;/em&gt;. Продемонстрируем это на примере трёх типовых задач.&lt;/p&gt;&lt;a class=anchor id=dotproduct&gt;&lt;/a&gt;&lt;h3&gt;Скалярное произведение сжатых векторов&lt;/h3&gt;&lt;p&gt;Первую задачу решим показательно «многословно», последовательно переходя от «наивной» реализации к пересвёртке.&lt;h4&gt;Условие задачи&lt;/h4&gt;&lt;blockquote&gt;&lt;p&gt;Даны 2 вектора целых чисел одинаковой длины, заданные в сжатой форме списками пар вида &lt;code&gt;(value, count)&lt;/code&gt;. Например, вектор &lt;code&gt;[4, 4, 5]&lt;/code&gt; задается как &lt;code&gt;[(4, 2), (5, 1)]&lt;/code&gt;. Необходимо посчитать скалярное произведение заданных векторов. Сжатие сохраняет исходный порядок элементов.&lt;/blockquote&gt;&lt;p&gt;Сигнатура функции:&lt;pre&gt;&lt;code class=scala&gt;def dotProduct(vec1: List[(Int, Int)], vec2: List[(Int, Int)]): Long&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Сценарии для тестирования:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code&gt;(List.empty[(Int, Int)], List.empty[(Int, Int)]) -&amp;gt; 0L&lt;/code&gt;,&lt;li&gt;&lt;p&gt;&lt;code&gt;(List((4, 2), (5, 1)), List((2, 1), (1, 2))) -&amp;gt; 17L&lt;/code&gt;,&lt;li&gt;&lt;p&gt;&lt;code&gt;(List((1, 999999)), List((1, 999999))) -&amp;gt; 999999L&lt;/code&gt;.&lt;/ul&gt;&lt;h4&gt;Наивное решение&lt;/h4&gt;&lt;p&gt;Давайте просто сперва развернём сжатые вектора в исходные и вычислим обычное скалярное произведение:&lt;pre&gt;&lt;code class=scala&gt;val getOrigView: List[(Int, Int)] =&amp;gt; View[Int] =  // Представление исходного вектора&#xA;  _.view.flatMap((i, n) =&amp;gt; View.fill(n)(i))  &#xA;&#xA;def simpleDotProduct(v1: Iterable[Int], v2: Iterable[Int]) =  &#xA;  (v1 `lazyZip` v2).map(_.toLong * _).sum  &#xA;&#xA;def dotProduct(vec1: List[(Int, Int)], vec2: List[(Int, Int)]): Long =  &#xA;  simpleDotProduct(getOrigView(vec1), getOrigView(vec2))&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Решение очень простое, прямо-таки тривиальное. Память алгоритма константна за счёт использования &lt;code&gt;View&lt;/code&gt; — это псевдоколлекция, которая не только лениво накапливает применяемые к ней преобразования, но и применяет их все поэлементно, не сохраняя в памяти промежуточные значения и результат. Оценка производительности стабильно пропорциональна длине &lt;em&gt;несжатых векторов&lt;/em&gt;.&lt;p&gt;Однако используемое в задаче сжатие вектора позволяет существенно оптимизировать вычисление скалярного произведения в случае, когда у векторов повторяются значения подряд идущих координат. Прогон такого алгоритма на втором тестовом сценарии со сжатыми векторами миллионной размерности демонстрирует на современных компьютерах задержку, заметную глазу, хотя в этом случае результат вычисляется в уме, причём гораздо быстрее.&lt;h4&gt;Рекурсия&lt;/h4&gt;&lt;p&gt;Нужно переписать алгоритм так, чтобы при расчёте использовались преимущества сжатых векторов. Воспользуемся функциональной интерпретацией &lt;em&gt;метода двух указателей&lt;/em&gt; (&lt;em&gt;two pointers&lt;/em&gt;): будем одновременно рассматривать головы обоих списков и сдвигать только тот указатель, чей текущий сжатый блок закончился. Сделаем это с помощью рекурсии:&lt;pre&gt;&lt;code class=scala&gt;def dotProduct(vec1: List[(Int, Int)], vec2: List[(Int, Int)]): Long =  &#xA;  (vec1.headOption `zip` vec2.headOption)  &#xA;    .fold(0L) { case ((i1, n1), (i2, n2)) =&amp;gt;  &#xA;      val ii = i1.toLong * i2  // важно не получить переполнение уже здесь&#xA;      val nn = n2 - n1  &#xA;&#xA;      if nn &amp;gt; 0 then ii * n1 + dotProduct(vec1.tail, (i2, nn) :: vec2.tail)  &#xA;      else           ii * n2 + dotProduct((i1, -nn) :: vec1.tail, vec2.tail)  &#xA;    }&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Если какой-то из списков пустой? то это не страшно — значит, либо другой тоже пустой, либо там будет единственная пара с нулевым количеством повторений, и в этом случае возвращаем &lt;code&gt;0&lt;/code&gt;. Иначе разбираем первые пары из обоих векторов: оптимизированно вычисляем промежуточную свёртку для первых &lt;code&gt;n1 min n2&lt;/code&gt; измерений, и остаётся лишь добавить свёртку оставшихся хвостов с учётом недосчитанного остатка от нашей оптимизации.&lt;p&gt;Тестовые сценарии второй алгоритм обработает успешно, причём заметно быстрее первого варианта. Однако, ввиду передачи параметров через стек, данный алгоритм потребляет память, пропорциональную длинам сжатых векторов, и если в исходном векторе миллионной размерности вообще не будет повторяющихся последовательных значений, то произойдёт переполнение стека…&lt;h4&gt;Хвостовая рекурсия&lt;/h4&gt;&lt;p&gt;Проблема обозначена, давайте решим её, переписав рекурсию в хвостовой форме, благо компилятор Scala умеет её оптимизировать. Для этого, в частности, вручную разберём &lt;code&gt;Option&lt;/code&gt; с «головами» векторов, применим ранний выход, а также нам придётся добавить в список аргументов аккумулятор результата:&lt;pre&gt;&lt;code class=scala&gt;@tailrec  &#xA;def dotProductRec(accum: Long, vec1: List[(Int, Int)], vec2: List[(Int, Int)]): Long =  &#xA;  val heads = vec1.headOption `zip` vec2.headOption  &#xA;  if heads.isEmpty then return accum // да, ранний выход&#xA;  &#xA;  val ((i1, n1), (i2, n2)) = heads.get  &#xA;  &#xA;  val nextVec1 = if n1 &amp;gt; n2 then (i1, n1 - n2) :: vec1.tail else vec1.tail  &#xA;  val nextVec2 = if n2 &amp;gt; n1 then (i2, n2 - n1) :: vec2.tail else vec2.tail  &#xA;  &#xA;  &#xA;  dotProductRec(accum + (n1 min n2).toLong * i1 * i2, nextVec1, nextVec2)  &#xA;end dotProductRec  &#xA;  &#xA;def dotProduct(vec1: List[(Int, Int)], vec2: List[(Int, Int)]): Long =  &#xA;  dotProductRec(0L, vec1, vec2)&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Вот такой реализации уже не страшны векторы больших размерностей, и потребляемая память не будет зависеть от длины векторов!&lt;h4&gt;Пересвёртка&lt;/h4&gt;&lt;p&gt;Из всех возможных реализаций рекурсии наиболее безопасной оказывается именно хвостовая. Чтобы вовсе с ней не заморачиваться, лучше сразу реализовывать алгоритм в терминах пересвёртки неподвижной точки конструктора типов.&lt;p&gt;Посмотрим на одну отдельную итерацию в &lt;code&gt;dotProductRec&lt;/code&gt;:&lt;ul&gt;&lt;li&gt;&lt;p&gt;в одном из вариантов останавливаем вычисления,&lt;li&gt;&lt;p&gt;в другом варианте вычисления продолжаются с изменившимся состоянием,&lt;li&gt;&lt;p&gt;результатом каждого шага вычислений будет &lt;code&gt;accum: Long&lt;/code&gt;. Отсюда следует, что искомый конструктор типов имеет знакомый вид &lt;code&gt;OptCell&lt;/code&gt;.&lt;/ul&gt;&lt;p&gt;На каждом шаге используется состояние такого вида:&lt;pre&gt;&lt;code class=scala&gt;type State = (accum: Long, vec1: List[(Int, Int)], vec2: List[(Int, Int)])&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Эти состояния вычисляются посредством коалгебры &lt;code&gt;OptCell&lt;/code&gt;:&lt;pre&gt;&lt;code class=scala&gt;val coalgebra: Coalgebra[OptCell[Long]][State] =  &#xA;  case (accum, vec1, vec2) =&amp;gt;  &#xA;    (vec1.headOption `zip` vec2.headOption)  &#xA;      .map { case ((i1, n1), (i2, n2)) =&amp;gt;  &#xA;        val nextAccum = accum + (n1 min n2).toLong * i1 * i2  &#xA;  &#xA;        val nextVec1 = if n1 &amp;gt; n2 then (i1, n1 - n2) :: vec1.tail else vec1.tail  &#xA;        val nextVec2 = if n2 &amp;gt; n1 then (i2, n2 - n1) :: vec2.tail else vec2.tail  &#xA;  &#xA;        nextAccum -&amp;gt; (nextAccum, nextVec1, nextVec2)  &#xA;      }  &lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Нас интересует только значение аккумулятора у самого последнего значения, либо ноль, если векторы пустые. Значит, в качестве сворачивающей алгебры выступит пара &lt;code&gt;(0L, lastAccum)&lt;/code&gt;.&lt;p&gt;Разобравшись с (ко)алгебрами, можно записать нерекурсивное стекобезопасное решение:&lt;pre&gt;&lt;code class=scala&gt;def dotProduct(vec1: List[(Int, Int)], vec2: List[(Int, Int)]): Long =  &#xA;  refoldIter(coalgebra, 0L, lastAccum)(0L, vec1, vec2)&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В итоге получаем те же метрики, что и у предыдущего алгоритма, но здесь вся рекурсия спрятана в пересвёртку.&lt;/p&gt;&lt;a class=anchor id=refuelingscount&gt;&lt;/a&gt;&lt;h3&gt;Количество заправок&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Есть список городов, заданный целочисленными координатами. Все города соединены дорогами только по вертикали и горизонтали (никаких диагоналей!). Автозаправочные станции есть только в городах. Одна единица топлива тратится на преодоление расстояния между соседними координатами (например, на путь &lt;code&gt;(5, 3) -&amp;gt; (6, 2)&lt;/code&gt; понадобится уже две единицы). Нужно определить минимальное количество дозаправок (включая стартовую), необходимых для поездки между двумя заданными городами, если известна также ёмкость бензобака автомобиля. Если на таком авто невозможно достичь пункта назначения, то вернуть &lt;code&gt;-1&lt;/code&gt;.&lt;/blockquote&gt;&lt;p&gt;Термины предметной области&lt;pre&gt;&lt;code class=scala&gt;type Town  = Int                    // номер города (начиная с 0)&#xA;type Dist  = Int                    // дистанция  &#xA;type Fuel  = Dist                   // количество топлива = дистанция  &#xA;type Coord = (x: Dist, y: Dist)     // координаты города  &#xA;type Route = (from: Town, to: Town) // маршрут  &#xA;&#xA;def dist(t1: Coord, t2: Coord): Dist = // расстояние между любыми городами  &#xA;  (t2.x - t1.x).abs + (t2.y - t1.y).abs&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Сигнатура искомой функции:&lt;pre&gt;&lt;code class=scala&gt;def refuelingsCount(&#xA;  towns: Vector[Coord], // список городов&#xA;  tank: Fuel,           // объём бензобака&#xA;  goalRoute: Route      // пункты отправления и назначения&#xA;): Int                  // кол-во дозаправок или -1&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Решать задачу будем &lt;a href=https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D0%B2_%D1%88%D0%B8%D1%80%D0%B8%D0%BD%D1%83 rel=&#34;noopener noreferrer nofollow&#34;&gt;поиском в ширину&lt;/a&gt; (Breadth-First Search, BFS). Сперва вычислим города, достижимые из стартового, а затем на каждом шаге будем искать непосещённые города, достижимые из найденных на предыдущем шаге. Как только встречаем целевой город, подсчитываем количество шагов — это и будет искомое количество дозаправок.&lt;p&gt;Мы как бы отправляем «волну поиска» из стартового города и по &lt;a href=https://ru.wikipedia.org/wiki/%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF_%D0%93%D1%8E%D0%B9%D0%B3%D0%B5%D0%BD%D1%81%D0%B0_%E2%80%94_%D0%A4%D1%80%D0%B5%D0%BD%D0%B5%D0%BB%D1%8F rel=&#34;noopener noreferrer nofollow&#34;&gt;принципу Гюйгенса&lt;/a&gt; каждый город фронта волны порождает собственные волны, образующие следующий фронт. Волна будет распространяться не обязательно радиально — в зависимости от конфигурации городов итоговый маршрут может иметь любую форму, даже спиральную. В итоге «двумерная» задача сводится к линейной итерации фронтов волны поиска.&lt;p&gt;Но в этих итерациях нам понадобятся дополнительные стандартные структуры данных — &lt;em&gt;индексированная&lt;/em&gt; коллекция координат городов &lt;code&gt;Vector[Coord]&lt;/code&gt; из начального условия, а также &lt;em&gt;множества&lt;/em&gt; непосещённых городов и фронта волны &lt;code&gt;Set[Town]&lt;/code&gt;. По сути, они являются оптимизированными функциями &lt;code&gt;Town =&amp;gt; Coord&lt;/code&gt; и &lt;code&gt;Town =&amp;gt; Boolean&lt;/code&gt; соответственно (хотя нужно ещё уметь «пробегать по фронту»).&lt;p&gt;Состояние, меняющееся от итерации к итерации — это множества непосещённых городов и фронта волны:&lt;pre&gt;&lt;code class=scala&gt;type State = (unvisited: Set[Town], front: Set[Town])&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Коалгебра, порождающая последовательность фронтов, будет иметь сигнатуру &lt;code&gt;Coalgebra[OptCell[Set[Town]]][State]&lt;/code&gt;. Если фронт пустой — прекращаем вычисления. Иначе для нового фронта вычисляем города, доступные из текущего.&lt;p&gt;Свёртку последовательности полезно останавливать, как только во фронте будет обнаружен целевой город. Значит, нам нужна пересвёртка &lt;code&gt;shortRefold&lt;/code&gt;, допускающая «ранний» выход без обязательного анализа всех городов.&lt;p&gt;В итоге получается такая реализация:&lt;pre&gt;&lt;code class=scala&gt;def calcRefuelings(towns: Vector[Coord], tank: Fuel, goalRoute: Route): Int =  &#xA;  &#xA;  // вспомогательная функция для удобства&#xA;  def canReachWithFullTank(t1: Town, t2: Town) = dist(towns(t1), towns(t2)) &amp;lt;= tank  &#xA;  &#xA;  val coalgebra: Coalgebra[OptCell[Set[Town]]][State] = state =&amp;gt;  &#xA;    state.front.headOption.map { _ =&amp;gt; // продолжаем, только если фронт не пустой    &#xA;      val newFront = state.unvisited  // вычисляем следующий фронт  &#xA;        .filter(nextTown =&amp;gt; state.front.exists( // добавляем город, если он&#xA;          canReachWithFullTank(_, nextTown)     // достижим с текущего фронта  &#xA;        ))  &#xA;      newFront -&amp;gt; ((state.unvisited -- newFront) -&amp;gt; newFront)  &#xA;    }  &#xA;  &#xA;  val shortAccum: (Int, Set[Town]) =&amp;gt; Either[Int, Int] = (counter, front) =&amp;gt;  &#xA;    Either.cond(front.contains(goalRoute.to), // если во фронте есть цель  &#xA;      counter,                                // то вернуть текущий счётчик  &#xA;      counter + 1                             // иначе продолжить дальше  &#xA;    )  &#xA;  &#xA;  val unvisitedTowns = Set.range(0, towns.length)&#xA;  val startFront  = Set(goalRoute.from) // «излучаем» из начала  &#xA;  &#xA;  val countedOrNot = shortRefold(coalgebra, 1, shortAccum)(unvisitedTowns, startFront)  &#xA;  &#xA;  countedOrNot.swap.getOrElse(-1)  &#xA;end calcRefuelings&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;И не видно никаких рекурсий!&lt;/p&gt;&lt;a class=anchor id=queens&gt;&lt;/a&gt;&lt;h3&gt;Задача о ферзях&lt;/h3&gt;&lt;blockquote&gt;&lt;p&gt;Нужно расставить n ферзей (разных цветов)) на шахматной доске n×n так, чтобы каждый чувствовал себя в безопасности — не оказался атакованным остальными ферзями.&lt;/blockquote&gt;&lt;p&gt;Термины предметной области:&lt;pre&gt;&lt;code class=scala&gt;type File = Int  // номер вертикали&#xA;type Rank = Int  // номер горизонтали (не понадобится)&#xA;type Setup = Vector[File]  // расстановка - номера вертикалей ферзей для каждой горизонтали&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В векторе &lt;code&gt;Setup&lt;/code&gt; индекс — это номер горизонтали, а значение — номер вертикали.&lt;p&gt;Сигнатура искомой функции:&lt;pre&gt;&lt;code class=scala&gt;def queensSetup(n: Int): List[Setup]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Идея решения задачи простая — это классический &lt;a href=https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B8%D1%81%D0%BA_%D1%81_%D0%B2%D0%BE%D0%B7%D0%B2%D1%80%D0%B0%D1%82%D0%BE%D0%BC rel=&#34;noopener noreferrer nofollow&#34;&gt;поиск с возвратом&lt;/a&gt; (backtracking). В нашей терминологии он превращается в развёртку дерева всех корректных расстановок ферзей. Для этого нужно развернуть дерево всех корректных расстановок ферзей и собрать его «листья», со всеми n ферзями. Разворачивать будем так:&lt;ul&gt;&lt;li&gt;&lt;p&gt;для уже расставленных на предыдущих горизонталях ферзей проверяем следующую горизонталь,&lt;li&gt;&lt;p&gt;проверяем установку нового ферзя на каждой вертикали;&lt;li&gt;&lt;p&gt;только если получилась корректная расстановка, продолжаем развёртывание уже для неё.&lt;/ul&gt;&lt;p&gt;Нам потребуется функция проверки корректности добавления ферзя на указанную вертикаль следующей горизонтали:&lt;pre&gt;&lt;code class=scala&gt;def isNewQueenUnderAttack(filledRanks: Setup, newRankFile: File): Boolean =  &#xA;  filledRanks.zipWithIndex.exists((rankFile, rank) =&amp;gt;  &#xA;    val d = filledRanks.length - rank  &#xA;&#x9;    Array(  // не очень эффективно, зато идиоматично&#xA;      newRankFile,  &#xA;      newRankFile + d,  &#xA;      newRankFile - d  &#xA;    ).contains(rankFile)  &#xA;  )&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Также нам понадобится дерево, а точнее, описание только одного его узла:&lt;pre&gt;&lt;code class=scala&gt;type TreeBase[A] = [T] =&amp;gt;&amp;gt; Option[(current: A, branches: List[T])] // Rose Tree&#xA;//type Tree[A] = 𝛎[TreeBase[A]]&#xA;given treeBaseFunctor: [X] =&amp;gt; Functor[TreeBase[X]] =  &#xA;  [A, B] =&amp;gt; (f: A =&amp;gt; B) =&amp;gt; _.map((a, lst) =&amp;gt; a -&amp;gt; lst.map(f))&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;При развёртывании дерева мы оставляем в каждом узле только корректные дочерние расстановки:&lt;pre&gt;&lt;code class=scala&gt;val coalg: Coalgebra[TreeBase[Setup]][Setup] =  &#xA;  (ranks: Setup) =&amp;gt;  &#xA;    Option.when(ranks.length &amp;lt;= n)(  &#xA;      ranks -&amp;gt; Iterator.range(0, n)  &#xA;        .filterNot(isNewQueenUnderAttack(ranks, _))  &#xA;        .map(ranks.appended)  &#xA;        .toList  &#xA;    )&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Затем нужно свернуть дерево, выбирая лишь «листья», у которых расстановка содержит ровно &lt;code&gt;n&lt;/code&gt; строк. В данной алгебре применяется маленький трюк — конкатенация &lt;code&gt;++&lt;/code&gt; позволяет одной строкой обработать две ситуации: искомую расстановку n×n и продолжение поиска.&lt;pre&gt;&lt;code class=scala&gt;val alg: Algebra[TreeBase[Setup]][List[Setup]] =  &#xA;  _.toList.flatMap(node =&amp;gt;  &#xA;    List(node.current).filter(_.length == n) ++  &#xA;      node.branches.flatten  &#xA;  )&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Подразумевается, что уровень вложенности рекурсии, определяемый &lt;code&gt;n&lt;/code&gt;, будет небольшим, поэтому для пересвёртки достаточно использовать функцию &lt;code&gt;hylo&lt;/code&gt;, использующую стек. В итоге получается следующее решение задачи:&lt;pre&gt;&lt;code class=scala&gt;def queensSetup(n: Int): List[Setup] =  &#xA;  &#xA;  val coalg: Coalgebra[TreeBase[Setup]][Setup] = ...&#xA;  val   alg:   Algebra[TreeBase[Setup]][List[Setup]] = ...&#xA;  &#xA;  hylo(coalg, alg)(Vector.empty) // начинаем с пустой расстановки&#xA;end queensSetup&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;В редких задачах могут встречаться несбалансированные деревья огромной высоты. Тогда либо придётся воспользоваться функцией &lt;code&gt;safeHylo&lt;/code&gt;, либо попробовать другой путь. Обход рекурсивной структуры так или иначе сводится к последовательным итерациям, между которыми зачастую приходится передавать сложные структуры данных. В &lt;code&gt;safeHylo&lt;/code&gt; это достигается посредством &lt;code&gt;SwapIn&lt;/code&gt; («пробегание» по функтору &lt;code&gt;F&lt;/code&gt;), а деревья итерируемых состояний строятся с помощью &lt;code&gt;TailRec&lt;/code&gt;. Но того же результата всегда можно добиться и вручную.&lt;p&gt;Чтобы линейно пробежать рекурсивную структуру данных, нужно уметь передавать фокус с текущего узла на следующий непосещённый. В пятой части обзора говорилось, что тип фокуса вычисляется как &lt;a href=https://habr.com/ru/articles/863376/#rec_derivatives rel=&#34;noopener noreferrer nofollow&#34;&gt;производная от типа рекурсивных структур&lt;/a&gt;. Одним из элементов такой производной является «список ветвлений», пройденных от корня до выбранного узла. Этот список позволяет «сделать шаг назад» и выбрать новый путь.&lt;details class=spoiler&gt;&lt;summary&gt;«Линейное» решение задачи о ферзях&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;В задаче о ферзях не обязательно хранить информацию о ветвлениях по той причине, что вся информация и так содержится в значении &lt;code&gt;Setup&lt;/code&gt; каждого узла. Тогда «линейное» решение задачи можно записать так:&lt;pre&gt;&lt;code class=scala&gt;def queensSetupIter(n: Int): List[Setup] =  &#xA;  type State = (List[Setup], Setup)  &#xA;    &#xA;  val checkedSetup: Coalgebra[Option][Setup] = rankFiles =&amp;gt;  &#xA;    Some(rankFiles).filterNot(_  &#xA;      .init.zipWithInddef queensSetupIter(n: Int): List[Setup] =  &#xA;  type State = (List[Setup], Setup)  &#xA;    &#xA;  val checkedSetup: Coalgebra[Option][Setup] = rankFiles =&amp;gt;  &#xA;    Some(rankFiles).filterNot(_  &#xA;      .init.zipWithIndex.exists((rankFile, rank) =&amp;gt;  &#xA;        val d = rankFiles.length - rank - 1  &#xA;        Array(  // не очень эффективно, зато идиоматично&#xA;          rankFiles.last,  &#xA;          rankFiles.last + d,  &#xA;          rankFiles.last - d  &#xA;        ).contains(rankFile)  &#xA;      )  &#xA;    )  &#xA;  &#xA;  def nextPrevFile(setup: Setup) =  &#xA;    setup.lastOption.map(file =&amp;gt; setup.init :+ (file + 1))  &#xA;  &#xA;  def coalg: Coalgebra[OptCell[List[Setup]]][State] =  &#xA;    (res, setup) =&amp;gt; // промежуточный результат и текущая расстановка  &#xA;      if setup.last == n then // вышли за границы поля  &#xA;        nextPrevFile(setup.init).map(stp =&amp;gt; res -&amp;gt; (res -&amp;gt; stp))  &#xA;      else {  &#xA;        val correctSetupOpt = checkedSetup(setup) // валидация расстановки  &#xA;        val newRes = // новый результат  &#xA;          if setup.length == n then correctSetupOpt.toList ++ res  &#xA;          else res  &#xA;        val nextSetup = // следующая расстановка  &#xA;          if setup.length &amp;lt; n &amp;amp;&amp;amp; correctSetupOpt.nonEmpty then Some(setup :+ 0)  &#xA;          else nextPrevFile(setup)  &#xA;  &#xA;        nextSetup.map(stp =&amp;gt; newRes -&amp;gt; (newRes -&amp;gt; stp))  &#xA;      }  &#xA;&#xA;  refoldIter(coalg, Nil, lastAccum)(Nil, Vector(0))  &#xA;  &#xA;end queensSetupIter&#xA;ex.exists((rankFile, rank) =&amp;gt;  &#xA;        val d = rankFiles.length - rank - 1  &#xA;        Array(  // не очень эффективно, зато идиоматично&#xA;          rankFiles.last,  &#xA;          rankFiles.last + d,  &#xA;          rankFiles.last - d  &#xA;        ).contains(rankFile)  &#xA;      )  &#xA;    )  &#xA;  &#xA;  def nextPrevFile(setup: Setup) =  &#xA;    setup.lastOption.map(file =&amp;gt; setup.init :+ (file + 1))  &#xA;  &#xA;  def coalg: Coalgebra[OptCell[List[Setup]]][State] =  &#xA;    (res, setup) =&amp;gt; // промежуточный результат и текущая расстановка  &#xA;      if setup.last == n then // вышли за границы поля  &#xA;        nextPrevFile(setup.init).map(stp =&amp;gt; res -&amp;gt; (res -&amp;gt; stp))  &#xA;      else {  &#xA;        val correctSetupOpt = checkedSetup(setup) // валидация расстановки  &#xA;        val newRes = // новый результат  &#xA;          if setup.length == n then correctSetupOpt.toList ++ res  &#xA;          else res  &#xA;        val nextSetup = // следующая расстановка  &#xA;          if setup.length &amp;lt; n &amp;amp;&amp;amp; correctSetupOpt.nonEmpty then Some(setup :+ 0)  &#xA;          else nextPrevFile(setup)  &#xA;  &#xA;        nextSetup.map(stp =&amp;gt; newRes -&amp;gt; (newRes -&amp;gt; stp))  &#xA;      }  &#xA;&#xA;  refoldIter(coalg, Nil, lastAccum)(Nil, Vector(0))  &#xA;  &#xA;end queensSetupIter&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/details&gt;&lt;a class=anchor id=summary&gt;&lt;/a&gt;&lt;h2&gt;Выводы&lt;/h2&gt;&lt;p&gt;Пересвёртка неподвижных точек конструкторов типов является &lt;strong&gt;методом&lt;/strong&gt; решения задач динамического программирования (что бы ни имелось в виду)). Его преимущества заключаются в том, что все итерации оказываются спрятанными внутрь стандартных функций пересвёртки. Это избавляет программистов от ручной реализации рекурсии и циклов, защищая код от самой возможности появления многих ошибок. Для решения задачи остаётся только:&lt;ul&gt;&lt;li&gt;&lt;p&gt;сформулировать понятия предметной области,&lt;li&gt;&lt;p&gt;определиться со структурой ветвления (функтор &lt;code&gt;F[_]&lt;/code&gt;),&lt;li&gt;&lt;p&gt;описать шаг развёртки &lt;code&gt;Coalgebra[F]&lt;/code&gt;,&lt;li&gt;&lt;p&gt;описать шаг свёртки &lt;code&gt;Algebra[F]&lt;/code&gt;,&lt;li&gt;&lt;p&gt;указать граничные значения (&lt;code&gt;seedB&lt;/code&gt; для начала развёртки и &lt;code&gt;defaultC&lt;/code&gt; как часть алгебры),&lt;li&gt;&lt;p&gt;и выбрать наиболее подходящую функцию пересвёртки с учётом конкретного &lt;code&gt;F&lt;/code&gt; и ожидаемой глубины рекурсии.&lt;/ul&gt;&lt;p&gt;Из наших пересвёрток выпало явное упоминание неподвижной точки. Хотя само это понятие остаётся ключевым, здесь мы уже не зависим от его конкретной реализации. Впрочем, самостоятельные рекурсивные типы (тот же &lt;code&gt;List&lt;/code&gt;) по-прежнему необходимы как структуры данных — их можно хранить в памяти для повторного использования или сериализовать для передачи в другой процесс.&lt;p&gt;С более высокого уровня абстракций проблемы выглядят мельче))&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>Underskyer1</author>
      <guid>https://habr.com/ru/articles/1029528/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029528</guid>
      <pubDate>Thu, 30 Apr 2026 12:04:16 +0000</pubDate>
    </item>
    <item>
      <title>Установка macOS. Часть 1</title>
      <link>https://habr.com/ru/companies/ringo_mdm/articles/1029996/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029996</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/cd5/8a6/8c2/cd58a68c222e9d320f114a752042bfd0.png width=1670 height=942 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/cd5/8a6/8c2/cd58a68c222e9d320f114a752042bfd0.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/cd5/8a6/8c2/cd58a68c222e9d320f114a752042bfd0.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;«Семь бед - один ресет», — гласят пословицы. Если с вашим компьютером что-то не так или вы купили устройство с рук, то надо переустановить систему. Однако, переустановить macOS — это не одно действие, а как минимум шесть разных сценариев с разными последствиями для данных, прошивки и работоспособности машины.&lt;p&gt;В этой статье я разберу все основные методы: от самого быстрого («Стереть контент и настройки») до самого радикального (Restore через второй Mac с DFU-кабелем). &lt;h4&gt;Проверка системных требований&lt;/h4&gt;&lt;p&gt;Прежде чем что-то делать, убедитесь, что ваш Mac поддерживает нужную версию macOS.&lt;p&gt;&lt;strong&gt;macOS 26 (Tahoe)&lt;/strong&gt; — актуальная версия на момент написания статьи. Она поддерживает:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Все Mac на &lt;strong&gt;Apple Silicon&lt;/strong&gt; (M1 и новее).&lt;li&gt;&lt;p&gt;Ограниченный круг &lt;strong&gt;Intel-машин&lt;/strong&gt; — только с чипом &lt;strong&gt;T2. &lt;/strong&gt;Это последняя версия macOS с поддержкой Intel. &lt;/ul&gt;&lt;p&gt;Что проверить перед установкой:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Модель Mac: меню Apple → Об этом Mac (&lt;strong&gt;About This Mac)&lt;/strong&gt;&lt;li&gt;&lt;p&gt;Свободное место на диске: минимум 20–25 ГБ&lt;li&gt;&lt;p&gt;Стабильное интернет-соединение&lt;/ul&gt;&lt;p&gt;Полный список совместимых моделей: &lt;a href=http://everymac.com&gt;everymac.com&lt;/a&gt;&lt;h4&gt;Как работает «Стереть контент и настройки» и почему этого может быть не достаточно?&lt;/h4&gt;&lt;p&gt;Это самый быстрый способ вернуть Mac в исходное состояние без переустановки системы. Функция удаляет всех пользователей, их данные и установленные приложения, оставляя нетронутой саму macOS. После этого Mac встречает следующего владельца экраном «Ассистента настройки», как будто только что распакован из коробки. &lt;a href=https://support.apple.com/ru-ru/102664&gt;Подробнее в статье из документации Аpple&lt;/a&gt;.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/87e/6de/8aa/87e6de8aa930a01102680a70562ffb10.png alt=&#39;Рис.1 - Настройки macOS, расположение функции &#34;Стереть контент и настройки&#34;.&#39; title=&#39;Рис.1 - Настройки macOS, расположение функции &#34;Стереть контент и настройки&#34;.&#39; width=1658 height=1222 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/87e/6de/8aa/87e6de8aa930a01102680a70562ffb10.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/87e/6de/8aa/87e6de8aa930a01102680a70562ffb10.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.1 - Настройки macOS, расположение функции &amp;#34;Стереть контент и настройки&amp;#34;.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/645/bb7/52f/645bb752f95f47d01eaad687a12a6dc9.png alt=&#39;Рис.2 - Запуск функции &#34;Стереть контент и настройки&#34;.&#39; title=&#39;Рис.2 - Запуск функции &#34;Стереть контент и настройки&#34;.&#39; width=1556 height=1164 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/645/bb7/52f/645bb752f95f47d01eaad687a12a6dc9.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/645/bb7/52f/645bb752f95f47d01eaad687a12a6dc9.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.2 - Запуск функции &amp;#34;Стереть контент и настройки&amp;#34;.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В стандартных сценариях этого полностью достаточно. Но есть ситуации, когда без полноценной переустановки не обойтись.&lt;p&gt;&lt;strong&gt;Проблемы с Secure Token.&lt;/strong&gt; Secure Token — это атрибут учётной записи, необходимый для работы FileVault и ряда других механизмов безопасности. Если единственный пользователь с активным токеном был удалён, Mac может оказаться в нерабочем состоянии: система не разблокируется, а восстановить работоспособность без переустановки macOS практически невозможно.&lt;p&gt;&lt;strong&gt;Проблемы с Secure Enclave.&lt;/strong&gt; Ошибки при входе, сбои при активации, невозможность авторизоваться — всё это может указывать на повреждение данных на уровне &lt;a href=https://support.apple.com/ru-ru/guide/security/sec59b0b31ff/web&gt;Secure Enclave&lt;/a&gt;. Здесь поможет только полная переустановка, а в тяжёлых случаях — Restore через второй Mac.&lt;p&gt;&lt;strong&gt;Сбой в процессе стирания.&lt;/strong&gt; Любые сбои приводящие к “окирпичиванию” устройства, например, отключение питания в процессе стирания, обновления и тд.&lt;p&gt;&lt;strong&gt;Массовая подготовка устройств.&lt;/strong&gt; Если у вас парк Mac с устаревшей ОС и требования ИБ предписывают установку актуальной версии, то нет смысла поэтапно обновляться через загрузку апдейтов, особенно если не стоит цели сохранить пользовательские данные. Проще сразу поставить свежую macOS с нуля.&lt;p&gt;Далее перейдем к описанию способов установки macOS.&lt;h4&gt;Загрузочная флешка&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Когда использовать:&lt;/strong&gt; нет стабильного интернета, нужна конкретная версия macOS, массовая установка на несколько машин.&lt;p&gt;Все администраторы знакомы с созданием загрузочных флешек для Windows или Linux — для Mac процесс устроен аналогично. Подробное руководство можно найти &lt;a href=https://support.apple.com/ru-ru/101578&gt;в документации Apple.&lt;/a&gt;&lt;p&gt;Но если описывать этот процесс, то вы загружаете образ необходимой вам OC, после одной командой в терминале создаете загрузочную флешку (Рис.3), далее подключаете к выключенному Mac и удерживайте кнопку питания. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e41/951/1f2/e419511f21cd8f21d2edb5f8ffdb0d5f.png alt=&#34;Создание загружаемого установщика для macOS Ventura в приложении «Терминал».&#34; title=&#34;Рис.3 Создание загружаемого установщика для macOS Ventura в приложении «Терминал».&#34; width=1168 height=742 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e41/951/1f2/e419511f21cd8f21d2edb5f8ffdb0d5f.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e41/951/1f2/e419511f21cd8f21d2edb5f8ffdb0d5f.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.3 Создание загружаемого установщика для macOS Ventura в приложении «Терминал».&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Пока вы ее удерживаете, компьютер Mac запустится и загрузит &lt;a href=https://support.apple.com/ru-ru/102342&gt;параметры запуска&lt;/a&gt;, в которых отобразятся загружаемые тома, в том числе и загружаемый установщик.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Выберите загружаемый установщик и нажмите «Продолжить». &lt;li&gt;&lt;p&gt;Когда откроется установщик macOS, следуйте инструкциям на экране.&lt;/ol&gt;&lt;p&gt;Для компьютеров на базе Intel процесс несколько отличается, однако в рамках этой статьи мы не будем его рассматривать, поскольку, начиная со следующей версии macOS (27), такие устройства перейдут в разряд устаревших и не будут её поддерживать.&lt;p&gt;Вы также можете создать загрузочную флешку macOS, используя Windows, подробнее об этом можно почитать в &lt;a href=&#34;https://remontka.pro/macos-sierra-bootable-usb/#:~:text=%D0%9A%D0%B0%D0%BA%20%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B0%D1%82%D1%8C%20MacOS%20Sierra%20%D0%BD%D0%B0%20%D1%84%D0%BB%D0%B5%D1%88%D0%BA%D1%83%20%D0%B2%20Windows%2010%2C%208%20%D0%B8%20Windows%207&#34;&gt;этой статье&lt;/a&gt;.&lt;h4&gt;Переустановка через Recovery&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Когда использовать:&lt;/strong&gt; Mac не загружается, нужно переустановить систему поверх существующей или сделать чистую установку.&lt;p&gt;Это cамый простой способ для большинства ситуаций. &lt;a href=https://support.apple.com/ru-ru/102518&gt;Официальная документация.&lt;/a&gt;&lt;p&gt;&lt;strong&gt;Как войти в Recovery&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;На Apple Silicon&lt;/strong&gt;: зажмите кнопку питания, дождитесь меню загрузки и выберите &lt;strong&gt;Options.&lt;/strong&gt;&lt;p&gt;Из Recovery (Рис.4) можно:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Переустановить macOS.&lt;li&gt;&lt;p&gt;Стереть диск через Disk Utility.&lt;li&gt;&lt;p&gt;Восстановить из Time Machine.&lt;li&gt;&lt;p&gt;Запустить Terminal для диагностики.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/6bb/2c7/0cd/6bb2c70cdc53cd82b8cb041729ffd1f8.png alt=&#34;Рис.4 - Окно Recovery.&#34; title=&#34;Рис.4 - Окно Recovery.&#34; width=2048 height=1078 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/6bb/2c7/0cd/6bb2c70cdc53cd82b8cb041729ffd1f8.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/6bb/2c7/0cd/6bb2c70cdc53cd82b8cb041729ffd1f8.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.4 - Окно Recovery.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Важный нюанс:&lt;/strong&gt; по умолчанию Recovery устанавливает macOS &lt;strong&gt;поверх существующей&lt;/strong&gt;, не удаляя данные пользователей. Если нужна чистая установка, то сначала вручную сотрите диск через Disk Utility и только потом запускайте установку.&lt;h4&gt;Internet Recovery&lt;/h4&gt;&lt;p&gt;Когда локальный Recovery поврежден или недоступен. В этом случае Mac загружает минимальную Recovery-среду прямо с серверов Apple.&lt;p&gt;Посмотреть, как это выглядело на Intel Mac можно &lt;a href=https://www.idownloadblog.com/2016/02/25/how-to-start-up-your-mac-in-internet-recovery-mode/&gt;в этой статье&lt;/a&gt;.&lt;p&gt;&lt;strong&gt;На Apple Silicon &lt;/strong&gt;Internet Recovery интегрирован в стандартный механизм — при недоступности локального Recovery система автоматически обращается к серверам Apple.&lt;p&gt;&lt;strong&gt;Особенности:&lt;/strong&gt; загрузка может занять значительное время в зависимости от скорости соединения. &lt;p&gt;&lt;strong&gt;Fallback Recovery (только Apple Silicon)&lt;/strong&gt;&lt;p&gt;На Apple Silicon есть дополнительный уровень защиты — &lt;strong&gt;Fallback Recovery&lt;/strong&gt;. Это резервная Recovery-система, которая хранится отдельно от основного раздела Recovery (Рис.5).&lt;p&gt;Она активируется автоматически, если основной Recovery повреждён или не запускается. Пользователю почти ничего не нужно делать — система сама определяет, какой уровень Recovery использовать.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d70/486/cd1/d70486cd14647cf8d28908747c552e67.png alt=&#34;Use Fallback Recovery on Apple silicon Macs – The Eclectic Light Company&#34; title=&#34;Рис.5 - схема структуры загрузочного диска в macOS 15 (на Apple Silicon)&#34; width=1369 height=1514 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d70/486/cd1/d70486cd14647cf8d28908747c552e67.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d70/486/cd1/d70486cd14647cf8d28908747c552e67.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.5 - схема структуры загрузочного диска в &lt;strong&gt;macOS 15 (на Apple Silicon)&lt;/strong&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Именно поэтому Apple Silicon-машины в большинстве случаев можно восстановить без дополнительного оборудования, что принципиально отличает их от Intel.Подробнее можно почитать, как это работает &lt;a href=https://eclecticlight.co/2026/02/24/use-fallback-recovery-on-apple-silicon-macs/&gt;в этой статье&lt;/a&gt;.&lt;br&gt;&lt;br&gt;Единственное, хочется отметить, что начиная с установщиков macOS Big Sur 11.6.1 и Monterey, при обновлении основной Recovery система может копировать содержимое старой версии Recovery в Fallback Recovery. Это решение заранее заложено в обновлениях и не настраивается пользователем.&lt;p&gt;Это означает, что новый Mac на Apple Silicon может вообще не иметь Fallback Recovery до первого обновления macOS. Apple практически не документирует это поведение. &lt;h4&gt;Revive и Restore через второй Mac&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Когда использовать:&lt;/strong&gt; Mac не загружается вообще, показывает пустой экран, кружок с восклицательным знаком или индикатор состояния режима восстановления прошивки.&lt;p&gt;В редких случаях компьютер Mac с чипом Apple или Apple T2 Security Chip может столкнуться с проблемой, для устранения которой требуется регенерация либо восстановление прошивки с помощью другого компьютера Mac.&lt;p&gt;Такие проблемы могут возникнуть при определенных редких обстоятельствах (например, если установка операционной системы macOS прерывается вследствие сбоя питания):&lt;ol&gt;&lt;li&gt;&lt;p&gt;При запуске компьютера отображается &lt;a href=https://support.apple.com/ru-ru/102164&gt;кружок с восклицательным знаком&lt;/a&gt;.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/b43/0c4/8c4/b430c48c4d7c2d028f1a03870dc4a692.png alt=&#34;Рис.6 - Окно с восклицательным знаком после включения Mac&#34; title=&#34;Рис.6 - Окно с восклицательным знаком после включения Mac&#34; width=1280 height=832 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/b43/0c4/8c4/b430c48c4d7c2d028f1a03870dc4a692.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/b43/0c4/8c4/b430c48c4d7c2d028f1a03870dc4a692.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.6 - Окно с восклицательным знаком после включения Mac&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;li&gt;&lt;p&gt;Компьютер включается, но при запуске отображается пустой экран, а проблему не удается устранить с помощью других &lt;a href=https://support.apple.com/ru-ru/102575&gt;соответствующих решений&lt;/a&gt;.&lt;li&gt;&lt;p&gt;Для режима восстановления прошивки отображается &lt;a href=https://support.apple.com/ru-ru/102768&gt;индикатор состояния&lt;/a&gt;.&lt;li&gt;&lt;p&gt;При установке macOS возникает проблема, которую не удается устранить с помощью других &lt;a href=https://support.apple.com/ru-ru/102531&gt;соответствующих решений&lt;/a&gt;.&lt;/ol&gt;&lt;p&gt;При использовании такого способа важно знать:&lt;p&gt;Во-первых, есть различия между Revive и Restore.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;td&gt;&lt;p align=left&gt;Revive&lt;td&gt;&lt;p align=left&gt;Restore&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Что делает&lt;td&gt;&lt;p align=left&gt;Обновляет firmware и recoveryOS&lt;td&gt;&lt;p align=left&gt;Полный сброс firmware + recovery + macOS&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Данные&lt;td&gt;&lt;p align=left&gt;Сохраняются&lt;td&gt;&lt;p align=left&gt;Стираются полностью&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Во-вторых, обратите внимание на требования, что вам необходимо:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Компьютер Mac, с которым возникла проблема (&lt;a href=https://support.apple.com/ru-ru/116943&gt;Mac с чипом Apple&lt;/a&gt; или &lt;a href=https://support.apple.com/ru-ru/103265&gt;Mac с чипом Apple T2 Security Chip&lt;/a&gt;) и для которого необходимо выполнить регенерацию или восстановление прошивки. &lt;strong&gt;Другие модели Mac не подходят.&lt;/strong&gt;&lt;li&gt;&lt;p&gt;Другой компьютер Mac любой модели с macOS Sonoma 14 или более поздней версии. Вы будете использовать этот Mac для регенерации или восстановления компьютера Mac, на котором возникла проблема. &lt;a href=https://support.apple.com/ru-ru/109033&gt;Определение версии macOS, установленной на компьютере Mac&lt;/a&gt;.&lt;li&gt;&lt;p&gt;Кабель USB-C/USB-C, поддерживающий как передачу данных, так и зарядку. Например, зарядный кабель Apple USB-C, который поставляется с некоторыми продуктами Apple. Он совместим с &lt;a href=https://support.apple.com/ru-ru/109523&gt;портами на компьютере Mac&lt;/a&gt;, поддерживающими разъем USB-C.&lt;em&gt; &lt;/em&gt;&lt;strong&gt;&lt;em&gt;Не используйте кабель Thunderbolt 3.&lt;/em&gt;&lt;/strong&gt;&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Обратите внимание, что &lt;/strong&gt;&lt;a href=https://support.apple.com/ru-ru/120694&gt;&lt;strong&gt;порт DFU&lt;/strong&gt;&lt;/a&gt;&lt;strong&gt; у каждого устройства свой. Если использовать не тот порт, восстановление запустить не получится. &lt;/strong&gt;Пример для MacBook Pro (Рис.7):&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/08c/185/d77/08c185d77e41262c668c3eaf8052dae1.png alt=&#34;MacBook Pro, вид слева&#34; title=&#34;Рис.7 - MacBook Pro, вид слева, порт DFU.&#34; width=1560 height=500 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/08c/185/d77/08c185d77e41262c668c3eaf8052dae1.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/08c/185/d77/08c185d77e41262c668c3eaf8052dae1.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.7 - MacBook Pro, вид слева, порт DFU.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Подробная процедура восстановления Mac одним из перечисленных выше способов приведена &lt;a href=&#34;https://support.apple.com/ru-ru/108900#:~:text=3-,%D0%9A%D0%B0%D0%BA%20%D0%BF%D0%BE%D0%B4%D0%B3%D0%BE%D1%82%D0%BE%D0%B2%D0%B8%D1%82%D1%8C%20%D0%BD%D0%BE%D1%83%D1%82%D0%B1%D1%83%D0%BA%20%D0%BA%C2%A0%D1%80%D0%B5%D0%B3%D0%B5%D0%BD%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D0%B8%20%D0%B8%D0%BB%D0%B8%20%D0%B2%D0%BE%D1%81%D1%81%D1%82%D0%B0%D0%BD%D0%BE%D0%B2%D0%BB%D0%B5%D0%BD%D0%B8%D1%8E,-%D0%9F%D0%BE%D0%B4%D0%BA%D0%BB%D1%8E%D1%87%D0%B8%D1%82%D0%B5&#34;&gt;в документации Apple&lt;/a&gt;. После подготовки проблемного Mac и его подключения к основному устройству откроется окно режима DFU (рис.8).&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/bf1/c5c/81f/bf1c5c81f748badd0bc3ef5aae8da2f2.png alt=&#34;Окно Finder: на боковой панели выбран компьютер Mac.&#34; title=&#34;Рис.8 - Окно Finder: DFU.&#34; width=1460 height=1004 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/bf1/c5c/81f/bf1c5c81f748badd0bc3ef5aae8da2f2.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/bf1/c5c/81f/bf1c5c81f748badd0bc3ef5aae8da2f2.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.8 - Окно Finder: DFU.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Apple рекомендует сначала попробовать Revive, поскольку он обычно занимает меньше времени и позволяет сохранить пользовательские данные в отличие от полного сброса через восстановление Mac.&lt;h4&gt;Apple Configurator 2 и CLI&lt;/h4&gt;&lt;p&gt;&lt;a href=&#34;https://apps.apple.com/us/app/apple-configurator/id1037126344?mt=12&#34;&gt;Apple Configurator 2&lt;/a&gt; (AC2) — основной инструмент для Revive/Restore (рис.9), но у него есть и консольный вариант: &lt;a href=&#34;https://support.apple.com/en-ph/guide/deployment/dep6f70f6647/web#:~:text=If%20you%20want,cfgutil%20man%20page.&#34;&gt;cfgutil&lt;/a&gt;. Он позволяет автоматизировать операции в рамках скриптов или MDM-пайплайнов.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c50/587/2eb/c505872ebf31934dfae416a1bd44ee43.png alt=&#34;Рис.9 - Apple Configurator 2.&#34; title=&#34;Рис.9 - Apple Configurator 2.&#34; width=1200 height=768 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c50/587/2eb/c505872ebf31934dfae416a1bd44ee43.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c50/587/2eb/c505872ebf31934dfae416a1bd44ee43.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.9 - Apple Configurator 2.&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Через AC2 также можно прошивать конкретный IPSW-файл. Это нужно, если требуется откатиться на определённую версию или поставить бета-прошивку. Подробнее про IPSW ниже.&lt;h4&gt;IPSW-прошивки&lt;/h4&gt;&lt;p&gt;&lt;a href=https://en.wikipedia.org/wiki/IPSW&gt;IPSW&lt;/a&gt; — это образы полной прошивки для устройств на базе Apple Silicon (по аналогии с тем, что давно используется для iPhone и iPad). При выполнении восстановления через AC2 на устройство устанавливается именно IPSW. Кроме того, при создании виртуальной машины, например, в &lt;a href=https://mac.getutm.app&gt;UTM&lt;/a&gt;, также потребуется загрузить IPSW-прошивку.&lt;p&gt;База актуальных IPSW для всех моделей Apple Silicon: &lt;a href=http://mrmacintosh.com&gt;mrmacintosh.com&lt;/a&gt;&lt;p&gt;Когда это нужно:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Нужная вам версия macOS уже не предлагается через стандартные каналы.&lt;li&gt;&lt;p&gt;Нужно воспроизвести конкретную конфигурацию, например, для тестирования или корпоративного деплоя).&lt;li&gt;&lt;p&gt;Restore через AC2 не находит нужную версию автоматически.&lt;/ul&gt;&lt;h4&gt;Инструменты для массового деплоя&lt;/h4&gt;&lt;p&gt;Если вы занимаетесь развёртыванием macOS на парке устройств, обратите внимание на &lt;a href=https://twocanoes.com/products/mac/mds/&gt;MDS (Mac Deploy Stick)&lt;/a&gt;. Это инструмент, который до версии 4 распространялся бесплатно, а начиная с версии 5 стал платным. Он позволяет создавать загрузочные флешки с автоматизированной установкой macOS и предварительной настройкой конфигурации, что упрощает работу (рис.10).&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/294/46f/4d3/29446f4d3cf5a2aa5be81f29b552da5b.png alt=&#34;Рис.10 - Окно MDS&#34; title=&#34;Рис.10 - Окно MDS&#34; width=1288 height=954 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/294/46f/4d3/29446f4d3cf5a2aa5be81f29b552da5b.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/294/46f/4d3/29446f4d3cf5a2aa5be81f29b552da5b.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Рис.10 - Окно MDS&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h4&gt;Заключение&lt;/h4&gt;&lt;p&gt;Надеюсь, этот краткий обзор инструментов и способов установки, восстановления и сброса macOS поможет вам сориентироваться и выбрать подходящий сценарий, а при необходимости углубиться в детали по приведённым в статье ссылкам и применить нужный метод на практике.&lt;p&gt;Но это далеко не все, что можно рассказать о процессе установки macOS, продолжение следует во второй части статьи. До встречи!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/ringo_mdm/articles/1029996/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029996</guid>
      <pubDate>Thu, 30 Apr 2026 12:00:41 +0000</pubDate>
    </item>
    <item>
      <title>Мемристор для Венеры: как ученые из США сделали память, которая не боится 700 °C</title>
      <link>https://habr.com/ru/companies/ru_mts/articles/1030038/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030038</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c96/009/539/c96009539d97897c5b35425701e8d682.png width=1200 height=675 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c96/009/539/c96009539d97897c5b35425701e8d682.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c96/009/539/c96009539d97897c5b35425701e8d682.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Есть у современной электроники одна очень человеческая слабость — она плохо переносит жару. Мы можем запускать нейросети с миллиардом параметров, строить огромные дата-центры и всерьез обсуждать колонизацию других планет, пока не мешает физика. К сожалению, вся вычислительная цивилизация сталкивается с ограничениями, когда внешняя температура поднимается выше 200 °C.&lt;p&gt;Однако исследователи из Университета Южной Калифорнии нашли выход. Они создали мемристор, который продолжает работать при температуре 700 °C — это выше температуры расплавленной лавы. О том, что вообще такое мемристор, как ученые сделали открытие, почему это новый шаг для развития ИИ и неужели люди скоро полетят на Венеру, — в статье.&lt;h2&gt;Жара как старый враг электроники&lt;/h2&gt;&lt;p&gt;Вся современная электроника живет в довольно узком температурном коридоре. Мы к этому настолько привыкли, что почти не замечаем горячий смартфон, шумный кулер у ноутбука или инфраструктуру охлаждения рядом с ЦОД. &lt;p&gt;Проблема в том, что тепло для электроники — это не просто вопрос комфорта: при росте температуры атомы в материалах становятся подвижнее, границы между слоями теряют стабильность, а утечки тока растут. В определенный момент устройство не столько ломается, сколько перестает быть предсказуемым, а для вычислений это почти одно и то же.&lt;p&gt;Именно поэтому большинство кремниевых чипов эффективны примерно до 125–150 °C, а уже после 200 °C начинают выходить из строя. Это не случайное ограничение и не недоработка инженеров, а следствие самой физики материалов, на которой построена вся индустрия. Мы научились компенсировать это охлаждением, защитой, резервированием, но по сути просто обошли проблему, а не решили ее. Однако решение есть, и скрыто оно в мемристоре. &lt;h2&gt;Мемистор, мемристор и старая мечта об умной памяти&lt;/h2&gt;&lt;p&gt;В начале 1960-х &lt;a href=https://en.wikipedia.org/wiki/Bernard_Widrow&gt;Бернард Уидроу&lt;/a&gt; занимался вещами, которые сегодня мы бы назвали ранним машинным обучением, хотя тогда это выглядело скорее как инженерный эксперимент на стыке электроники и психологии. Его система ADALINE пыталась имитировать работу нейрона через изменение весов, и для этого требовался элемент, который мог бы не просто проводить ток, а менять свое состояние в зависимости от того, что через него уже прошло. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/06e/0b7/27b/06e0b727b7e01e8c5f14367275a3073e.png width=1280 height=720 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/06e/0b7/27b/06e0b727b7e01e8c5f14367275a3073e.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/06e/0b7/27b/06e0b727b7e01e8c5f14367275a3073e.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Так появился &lt;strong&gt;мемистор&lt;/strong&gt; (да, именно так) — трехполюсное устройство с изменяемым сопротивлением, которое, по сути, «помнило» свою историю. Это была вполне прикладная штука, на которой работали реальные нейросетевые схемы вроде MADALINE.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/262/fae/db4/262faedb43acf2ff8e35dd0db10e5c5f.png alt=&#34;Cоветский  мемистор СЗР-2&#34; title=&#34;Cоветский  мемистор СЗР-2&#34; width=2048 height=1365 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/262/fae/db4/262faedb43acf2ff8e35dd0db10e5c5f.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/262/fae/db4/262faedb43acf2ff8e35dd0db10e5c5f.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Cоветский мемистор СЗР-2&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Через десять лет американский инженер-электрик &lt;a href=https://en.wikipedia.org/wiki/Leon_O._Chua&gt;Леон Чуа&lt;/a&gt; формулирует идею &lt;strong&gt;мемристора&lt;/strong&gt; как четвертого базового элемента электрической цепи. Не в смысле «еще одного транзистора», а недостающего звена в симметрии фундаментальных электрических величин. Если проще, его мемристор связывает заряд и магнитный поток так же, как резистор связывает ток и напряжение, — он не просто реагирует на сигнал, а хранит его след.&lt;p&gt;Долгое время эта идея оставалась теоретической. И только в конце 2000-х, когда HP Labs объявила о физической реализации мемристора на оксиде титана, тема снова всплыла на поверхность. Тогда ее подхватили с большим энтузиазмом, местами даже с избыточным, как это обычно бывает с технологиями, которым сразу пытаются назначить роль «убийцы всего существующего». Однако мемристоры не заменили транзисторы, не перевернули индустрию и не стали основой для ИИ (тогда). &lt;p&gt;Сейчас исследователи опять вернулись к теории мемристоров, но уже с вопросом не о том, как «научить» устройство, а о том, как вообще считать достаточно быстро и энергоэффективно и… желательно с жаропрочностью.&lt;h2&gt;Крошечный чип памяти для экстремальных температур &lt;/h2&gt;&lt;p&gt;В новом исследовании, опубликованном в журнале &lt;a href=https://www.science.org/doi/10.1126/science.aeb9934&gt;Science&lt;/a&gt;, ученые под руководством Джошуа Янга, профессора кафедры электротехники и вычислительной техники, сообщили о разработке нового типа электронного запоминающего устройства Gra/HfOx/W. Новый мемристор стабильно работал при температуре 700 °C — это намного выше всех предыдущих показателей в этой области.&lt;blockquote&gt;&lt;p&gt;«Можно назвать это революцией. Это лучшая высокотемпературная память из когда-либо продемонстрированных», — сообщил Янг.&lt;/blockquote&gt;&lt;h4&gt;Как случайная неудача стала физическим принципом &lt;/h4&gt;&lt;p&gt;У этой истории есть еще один приятный для науки поворот — мемристор Gra/HfOx/W появился на основе неудачи, а не как идеально предсказанный результат. Изначально ученые пытались сделать другое устройство на основе графена, но оно работало не так, как ожидалось. В обычной инженерной практике это момент, когда хочется тяжело вздохнуть, поправить установку, списать неудачу на еще одну попытку и пойти дальше… К счастью, в этом случае неудачный прототип решили не выбрасывать.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/12e/542/9d3/12e5429d3851c25c2436291ec4828567.png alt=&#34;Рассчитанная энергия адсорбции одиночного адатома и димера вольфрама, а также коэффициент диффузии вольфрама на поверхностях Pt(111) и Gra&#34; title=&#34;Рассчитанная энергия адсорбции одиночного адатома и димера вольфрама, а также коэффициент диффузии вольфрама на поверхностях Pt(111) и Gra&#34; width=1610 height=794 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/12e/542/9d3/12e5429d3851c25c2436291ec4828567.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/12e/542/9d3/12e5429d3851c25c2436291ec4828567.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Рассчитанная энергия адсорбции одиночного адатома и димера вольфрама, а также коэффициент диффузии вольфрама на поверхностях Pt(111) и Gra&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Команда начала разбираться, где именно система повела себя «неправильно», и выяснила, что дело в графене. Так и обнаружили функциональную границу, которая мешает вольфраму разрушать устройство изнутри. Материал, который много лет описывали как символ будущего, наконец оказался полезен. &lt;h4&gt;Устройство нового мемристора &lt;/h4&gt;&lt;p&gt;Устроен чип довольно просто и сложно одновременно: два электрода, между ними тонкий слой оксида, вся конструкция собрана в наноразмерный «сэндвич». Верхний электрод сделали из вольфрама — это один из самых жаростойких металлов, материал с температурой плавления, которой можно пугать менее подготовленные элементы таблицы Менделеева. Средний слой состоит из оксида гафния, хорошо знакомого полупроводниковой индустрии. Нижний электрод сделали из графена, того самого листа углерода толщиной в один атом.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/374/2ba/667/3742ba6676497450349f884f6c86f412.png alt=&#34;Устройство Gra/HfOx/W и изображение поперечного среза. (a) Оптическое изображение одного устройства размером ~1 мкм × 1 мкм. (b) Изображение поперечного среза, полученное с помощью просвечивающей электронной микроскопии, и спектроскопия электрохимического рассеяния для элементов W, Hf и C&#34; title=&#34;Устройство Gra/HfOx/W и изображение поперечного среза. (a) Оптическое изображение одного устройства размером ~1 мкм × 1 мкм. (b) Изображение поперечного среза, полученное с помощью просвечивающей электронной микроскопии, и спектроскопия электрохимического рассеяния для элементов W, Hf и C&#34; width=1280 height=468 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/374/2ba/667/3742ba6676497450349f884f6c86f412.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/374/2ba/667/3742ba6676497450349f884f6c86f412.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Устройство Gra/HfOx/W и изображение поперечного среза. (a) Оптическое изображение одного устройства размером ~1 мкм × 1 мкм. (b) Изображение поперечного среза, полученное с помощью просвечивающей электронной микроскопии, и спектроскопия электрохимического рассеяния для элементов W, Hf и C&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Графен здесь оказался важнее других элементов. В контрольных структурах с платиной атомы вольфрама находили удобную поверхность, закреплялись, собирались в кластеры и постепенно «строили» проводящий канал, который приводит память к короткому замыканию. На поверхности графена атомам вольфрама не за что зацепиться, связь получается слабой и неустойчивой.&lt;p&gt;За счет этого устройство продолжает работать как память даже при 700 °C. В эксперименте мемристор сохранял записанные состояния более 50 часов без обновления, выдерживал больше миллиарда циклов переключения, работал при напряжении около 1,5 В и переключался за десятки наносекунд. Это важно, потому что работа при высокой температуре мало что значит, если устройство быстро деградирует.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/52d/1cc/ee7/52d1ccee7a0219c9da16164932de86b6.png alt=&#34;(а) Схематическое изображение устройства Gra/HfOx/W. (b) Кривые переключения постоянного тока I-V устройств Gra/HfOx/W, измеренные на месте при различных температурах&#34; title=&#34;(а) Схематическое изображение устройства Gra/HfOx/W. (b) Кривые переключения постоянного тока I-V устройств Gra/HfOx/W, измеренные на месте при различных температурах&#34; width=1459 height=500 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/52d/1cc/ee7/52d1ccee7a0219c9da16164932de86b6.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/52d/1cc/ee7/52d1ccee7a0219c9da16164932de86b6.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;(а) Схематическое изображение устройства Gra/HfOx/W. (b) Кривые переключения постоянного тока I-V устройств Gra/HfOx/W, измеренные на месте при различных температурах&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Разработка принципиально отличается от традиционной кремниевой электроники. Даже специализированные решения (HTC, high-temperature silicon) обычно стабильно работают лишь в диапазоне примерно 225–300 °C и в редких случаях — до ~350 °C, тогда как большинство стандартных микросхем теряет работоспособность уже при 200 °C.&lt;p&gt;Отдельно исследователи собрали небольшую матрицу 32 на 32 элемента. По массиву ученые зафиксировали разброс характеристик, выход годных устройств и поведение элементов рядом друг с другом. То подтвердили работоспособность этих мемристоров. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/446/794/a96/446794a96a1ab173a824ab9b8c519739.png alt=&#34;Оптическое изображение крестообразной матрицы 32×32 (1K) на основе устройств Gra/HfOx/W с двухпроводной конфигурацией&#34; title=&#34;Оптическое изображение крестообразной матрицы 32×32 (1K) на основе устройств Gra/HfOx/W с двухпроводной конфигурацией&#34; width=713 height=465 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/446/794/a96/446794a96a1ab173a824ab9b8c519739.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/446/794/a96/446794a96a1ab173a824ab9b8c519739.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Оптическое изображение крестообразной матрицы 32×32 (1K) на основе устройств Gra/HfOx/W с двухпроводной конфигурацией&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Однако самое ценное в этой работе даже не рекорд температуры, а то, что его удалось объяснить. Команда использовала электронную микроскопию, спектроскопию и моделирование на квантовом уровне, чтобы понять, что происходит на границе между слоями. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/3c4/976/6cb/3c49766cbb3b83bcbae161c59c13a50c.png alt=&#34;Исследование механизма с помощью просвечивающей электронной микроскопии высокого разрешения, энергодисперсионной рентгеновской спектроскопии и рентгеновской спектроскопии с энергодисперсионной рентгеновской спектроскопией. Результаты испытаний на отжиг контрольных устройств Pt/HfOx/W&#34; title=&#34;Исследование механизма с помощью просвечивающей электронной микроскопии высокого разрешения, энергодисперсионной рентгеновской спектроскопии и рентгеновской спектроскопии с энергодисперсионной рентгеновской спектроскопией. Результаты испытаний на отжиг контрольных устройств Pt/HfOx/W&#34; width=1920 height=968 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/3c4/976/6cb/3c49766cbb3b83bcbae161c59c13a50c.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/3c4/976/6cb/3c49766cbb3b83bcbae161c59c13a50c.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Исследование механизма с помощью просвечивающей электронной микроскопии высокого разрешения, энергодисперсионной рентгеновской спектроскопии и рентгеновской спектроскопии с энергодисперсионной рентгеновской спектроскопией. Результаты испытаний на отжиг контрольных устройств Pt/HfOx/W&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h2&gt;При чем тут ИИ&lt;/h2&gt;&lt;p&gt;Хорошо, память выдерживает 700 °C, атомы ведут себя прилично, графен никого не пускает куда не надо. Но при чем здесь искусственный интеллект, кроме того, что его сейчас принято упоминать в любом контексте?&lt;p&gt;Большие языковые модели, какими бы сложными они ни казались на уровне архитектуры, в вычислительном смысле удивительно однообразны. Основная нагрузка сводится к матричным операциям — умножение, суммирование, снова умножение, и так миллиарды раз. Вся эта сложная логика распознавания изображений, генерации текста и поиска закономерностей в итоге разворачивается в повторяемый набор базовых операций.&lt;p&gt;Проблема в том, как эти операции выполняются. В классической цифровой архитектуре данные хранятся в памяти, а вычисления происходят в вычислительном блоке. Между ними постоянно идет обмен, который сам по себе стоит дорого. Чем больше модель, тем больше данных нужно передавать, тем выше энергопотребление и тем сильнее система греется.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/6a6/755/a6a/6a6755a6a013beaf44e3bd0ed5ad00d9.png width=1000 height=617 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/6a6/755/a6a/6a6755a6a013beaf44e3bd0ed5ad00d9.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/6a6/755/a6a/6a6755a6a013beaf44e3bd0ed5ad00d9.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Этот мемристор с чУдным названием Gra/HfOx/W хранит состояние в виде сопротивления, и это же состояние участвует в вычислении. Получается, входной сигнал задается напряжением, вес — проводимостью, а результат выходит в виде тока, который течет через элемент. То, что в цифровой системе разбивается на последовательность операций, здесь происходит как единый физический процесс. &lt;p&gt;Отсюда и интерес ученых к мемристорным массивам как к основе для вычислений в памяти. Они позволяют сократить расстояние между данными и вычислением, а значит — уменьшить энергопотребление и увеличить скорость на уровне архитектуры, а не за счет очередного прироста частоты или количества ядер. В контексте LLM это особенно важно, потому что масштаб начинает работать против нас: каждая неэффективность умножается на миллиарды операций.&lt;p&gt;Именно поэтому в разговоре об этом мемристоре ИИ появляется как логическое продолжение. Если память может считать (а считать можно там, где раньше было слишком горячо для электроники), то вычисления могут выйти за пределы привычных пространств.&lt;h2&gt;Где может пригодиться такая память&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Самый наглядный пример — Венера.&lt;/strong&gt; На эту планету человечество отправляет аппараты уже больше полувека, но каждый раз с одним и тем же сценарием: устройство садится, успевает передать немного данных и через короткое время выходит из строя. Причина не в том, что мы не умеем строить прочные конструкции или термостойкие корпуса. Проблема в электронике, которая в условиях температуры около 460 °C и высокого давления начинает деградировать быстрее, чем успевает выполнить свою работу. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/682/56f/3b6/68256f3b64b0ff052b54dcbafcbb8cf0.png width=636 height=358 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/682/56f/3b6/68256f3b64b0ff052b54dcbafcbb8cf0.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/682/56f/3b6/68256f3b64b0ff052b54dcbafcbb8cf0.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Возможно ли, что скоро на Венеру полетит человек? Нет, условия на Венере невыносимы для пилотируемой миссии… Однако если этот мемристор выйдет за пределы лаборатории, то часть вычислений можно оставить прямо на борту космического аппарата. Это не означает мгновенного прорыва в исследовании планет, но снимает одно из самых жестких ограничений: вместо того чтобы передавать поток сырых данных и надеяться, что они дойдут, устройство сможет обрабатывать их на месте и отправлять уже отфильтрованную информацию. &lt;p&gt;Второй пример применения экстремальные места и устройства на Земле — &lt;strong&gt;геотермальные скважины (где температура пород уходит в сотни градусов), ядерные и термоядерные установки, датчики в турбинах и двигателях, промышленные печи.&lt;/strong&gt; Везде сейчас стоят датчики, работающие по простому правилу: измеряем здесь — считаем где-то еще. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/9f2/632/a6d/9f2632a6db2def43af15db0441e792fe.png width=1280 height=720 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/9f2/632/a6d/9f2632a6db2def43af15db0441e792fe.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/9f2/632/a6d/9f2632a6db2def43af15db0441e792fe.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Высокотемпературная память с возможностью вычислений может разрушить это правило. Если устройство не только выдерживает среду, но и обрабатывает данные на месте, исчезает необходимость тащить все наружу. Это упростит архитектуру систем, снизит задержки и откроет сценарии, которые раньше были слишком дорогими или сложными.&lt;p&gt;Есть и менее драматичный, но не менее важный уровень применения. Автомобильная электроника, например, регулярно сталкивается с температурами около &lt;a href=https://community.infineon.com/t5/Knowledge-Base-Articles/What-is-AEC-Q100-and-it-s-Specifications/ta-p/248018&gt;100–125 °C&lt;/a&gt;, и там вопрос надежности уже давно стоит остро. Устройство, рассчитанное на 700 °C, в таких условиях будет работать с огромным запасом прочности. &lt;h2&gt;Жестокий, несправедливый мир &lt;/h2&gt;&lt;p&gt;Открытие сразу запускает фантазию: если чип выдерживает 700 °C, значит, можно отправлять аппараты на Венеру, оставлять их там на месяцы, а заодно встроить в них ИИ, который будет в реальном времени анализировать данные и принимать решения. Красиво и почти убедительно, но стоит немного притормозить…&lt;p&gt;Проблема в том, что новый мемристор — это только часть вычислительной системы, пусть и очень важная. Чтобы получить полноценного робота, способного работать в экстремальных условиях, нужна не только устойчивая память, но и логические элементы, интерфейсы, питание, упаковка, защита от деградации и, что не менее важно, возможность все это стабильно производить. &lt;p&gt;Есть и более приземленные ограничения. Даже в этом исследовании отмечается, что при длительной работе чипа процессы диффузии не исчезают полностью, а лишь замедляются. Устройства все равно в конечном итоге будут деградировать, пусть и гораздо медленнее, чем традиционные структуры. &lt;p&gt;Добавим сюда вопросы масштабирования, разброс характеристик в массивах, необходимость интеграции с другими элементами… В общем, до венерианского робота еще далеко.&lt;p&gt;Однако как только становится возможным что-то, что раньше было физически недостижимо, индустрия находит для этого не один, а десятки сценариев. Венера — самый яркий из них, но, возможно, не самый близкий.&lt;p&gt;&lt;strong&gt;&lt;em&gt;Делитесь в комментариях, где еще могут применяться такие мемристоры и через какое время, по вашему мнению, появятся венерианские роботы. &lt;/em&gt;&lt;/strong&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/ru_mts/articles/1030038/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030038</guid>
      <pubDate>Thu, 30 Apr 2026 12:00:28 +0000</pubDate>
    </item>
    <item>
      <title>Как не тратить ресурсы впустую: четыре шага перед разработкой любой фичи</title>
      <link>https://habr.com/ru/articles/1029804/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029804</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/dc5/940/99c/dc594099c7eb87e0d9fb02f81aa435ce.png width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/dc5/940/99c/dc594099c7eb87e0d9fb02f81aa435ce.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/dc5/940/99c/dc594099c7eb87e0d9fb02f81aa435ce.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Как сделать так, чтобы продукт или фича работала для пользователя? Ответ в этой статье.&lt;p&gt;Привет, на связи Саша Солдатов, CEO REB8T! Сегодня расскажу про четыре инструмента, которые наша команда использует в работе на каждом проекте: JTBD, ICE-приоритизацию, User Flow и UX-гипотезы.&lt;p&gt;Огромное спасибо нашему Lead UX/UI Вере Ксенофонтовой, которая собрала этот материал и разложила всё по полочкам для вас и нашей команды!&lt;p&gt;А теперь, начнём!&lt;h2&gt;1. Jobs To Be Done: думаем о пользователе&lt;/h2&gt;&lt;p&gt;JTBD — это фреймворк, который помогает понять, зачем пользователю нужен продукт и какую работу с его помощью выполняет. Вместо того чтобы думать о функциях, мы думаем о ситуации.&lt;p&gt;Когда заказчик приходит с ТЗ «добавьте личный кабинет», первое желание — открыть Figma и рисовать. Но в такие моменты надо себя останавливать и задавать вопросы:&lt;blockquote&gt;&lt;p&gt;Зачем пользователю личный кабинет? Что он хочет сделать? Что его не устраивает?&lt;/blockquote&gt;&lt;p&gt;Ответы могут быть разными:&lt;blockquote&gt;&lt;p&gt;Хочу видеть свой прогресс, Хочу быстро вернуться к нужному уроку, Хочу понимать, окупается ли обучение.&lt;/blockquote&gt;&lt;p&gt;Всё это — разные работы, и под каждую нужно своё решение.&lt;p&gt;&lt;strong&gt;Два ключевых инструмента внутри JTBD&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Job Story&lt;/strong&gt; — формулируем решение через ситуацию. Это задача в конкретном контексте.  Например:&lt;blockquote&gt;&lt;p&gt;Когда у меня мало свободного времени после работы, я хочу проходить уроки небольшими блоками, чтобы не бросить обучение через неделю.&lt;/blockquote&gt;&lt;p&gt;&lt;strong&gt;Job Map&lt;/strong&gt; — разбиваем работу на шаги, от осознания проблемы до результата. Для онлайн-обучения это выглядит так: &lt;em&gt;понял, что нужно прокачаться → выбрал платформу → записался → прошёл первые уроки → отслеживает прогресс → применяет знания → оценивает результат.&lt;/em&gt; Когда видишь цепочку, начинаешь замечать, где пользователь спотыкается и какие шаги продукт не поддерживает.&lt;p&gt;&lt;strong&gt;Как приоритизировать работы пользователя&lt;/strong&gt;&lt;p&gt;Как мы уже поняли, у пользователя не одно действие, а несколько. Нужно выбрать, с какого начать. Для этого оцениваем каждое по двум критериям:&lt;p&gt;— Насколько задача важна для пользователя (1–10 баллов)&lt;br&gt;— Насколько он доволен тем, как сейчас решает её (1–10 баллов)&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f8a/783/4f8/f8a7834f8feb8fff2b8975d663fc969e.png alt=&#34;В первую очередь берём в работу то, что важно, но решается плохо. Здесь продукт может обойти конкурентов&#34; title=&#34;В первую очередь берём в работу то, что важно, но решается плохо. Здесь продукт может обойти конкурентов&#34; width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f8a/783/4f8/f8a7834f8feb8fff2b8975d663fc969e.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f8a/783/4f8/f8a7834f8feb8fff2b8975d663fc969e.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;В первую очередь берём в работу то, что важно, но решается плохо. Здесь продукт может обойти конкурентов&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;На одном из проектов для онлайн-школы мы провели интервью с учениками и выделили три ключевые работы:&lt;ul&gt;&lt;li&gt;&lt;p&gt;«Хочу получить конкретные инструменты для работы» — важность 10, довольство 2&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;«Знать бэкграунд спикера, чтобы понять, насколько он научит» — важность 9, довольство 5&lt;/ul&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;«Быстро понять, есть ли нужный курс» — важность 7, довольство 3. &lt;/ul&gt;&lt;p&gt;JTBD не говорит, что именно делать. Он помогает понять, для чего делать. Разница кажется небольшой, но на практике она отделяет фичи, которыми пользуются, от тех, которые вводятся только для того, чтобы быть забытыми через 2 дня.&lt;h2&gt;2. ICE-приоритизация: берём в работу то, что даст максимум&lt;/h2&gt;&lt;p&gt;Когда фичей много, а ресурсы ограничены, нужен способ выбрать, с чего начать. ICE (Impact, Confidence, Ease) — фреймворк для приоритизации. Каждую фичу оцениваем по трём критериям от 1 до 10, а итоговый балл считается как их произведение.&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Impact (влияние)&lt;/strong&gt; — влияение фичи на ключевые метрики. LTV (суммарный доход от одного пользователя), Long-Term Retention (процент пользователей, которые остаются через N месяцев), Time to Value (время до первой полученной пользы).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confidence (уверенность)&lt;/strong&gt; — шанс, что фича сработает. Основывается на результатах интервью и опросов, наличии похожих решений у конкурентов, сильных пользовательских сигналах.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ease (простота)&lt;/strong&gt; — время и ресурсы на реализацию MVP-версии фичи.&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Два корректирующих коэффициента&lt;/strong&gt;&lt;p&gt;Помимо базовой формулы, добавляем два множителя, которые делают оценку точнее.&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Операционный коэффициент (0.1–1.0)&lt;/strong&gt; — снижаем приоритет, если фича требует постоянной поддержки, модерации или производства тяжёлого контента. Например, UGC с ручной модерацией получает коэффициент 0.8.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ad hoc корректировка (0.1–2.0)&lt;/strong&gt; — дополнительный множитель от команды или клиента. Если фича стратегически важна или уже есть договорённости с партнёрами — поднимаем выше 1.0. Если откладываем на потом — снижаем.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/dff/a94/ede/dffa94ede166ba0dae0adfc4b12fb950.png alt=&#34;Итоговая формула: ICE Score × Операционный коэффициент × Ad hoc = FINAL Score&#34; title=&#34;Итоговая формула: ICE Score × Операционный коэффициент × Ad hoc = FINAL Score&#34; width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/dff/a94/ede/dffa94ede166ba0dae0adfc4b12fb950.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/dff/a94/ede/dffa94ede166ba0dae0adfc4b12fb950.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Итоговая формула: ICE Score × Операционный коэффициент × Ad hoc = FINAL Score&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Пример из нашего проекта Mum&amp;#39;s Club&lt;/strong&gt;&lt;p&gt;Mum&amp;#39;s Club — приложение для молодых родителей. Нам нужно было выбрать, что добавить в него в первую очередь. Думали между двумя вариантами:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Статьи от экспертов.&lt;/strong&gt; Impact 9, Confidence 10, Ease 7. ICE = 630. Коэффициенты: 1.0 × 1.5. Итог: 945. Контент уже частично готов, эксперты проверены, клиент считает это ключевой частью бренда — поднимаем в приоритете.&lt;li&gt;&lt;p&gt;&lt;strong&gt;UGC-контент. &lt;/strong&gt;Impact 6, Confidence 7, Ease 8. ICE = 336. Коэффициенты: 0.8 × 0.1. Итог: 26.88. Идея интересная, но требует постоянной модерации, а клиент не готов запускать UGC в MVP — откладываем.&lt;/ul&gt;&lt;p&gt;Так мы поняли, что надо стартовать с экспертного контента. ICE помог не уходить в споры и сделать процесс выбора понятным для всей команды, как нашей, так и клиента.&lt;h2&gt;3. User Flow: карта пути пользователя от А до Б&lt;/h2&gt;&lt;p&gt;Когда мы знаем, что хочет сделать пользователь (JTBD), и выдвинули гипотезы о том, как это лучше реализовать, самое время нарисовать карту пути. User Flow — это схема движения пользователя внутри продукта. На этом этапе определяем, какие экраны видит, где принимает решения, как выбор меняет состояние интерфейса.&lt;p&gt;С помощью User Flow можно увидеть, какие действия спрятаны слишком далеко, где пользователь теряется, а какие шаги можно убрать или объединить.&lt;p&gt;&lt;strong&gt;Из чего состоит User Flow&lt;/strong&gt;&lt;p&gt;Момент первого контакта, будь то открытие приложение, переход по ссылке, нажатие на баннер. Дальше идут шаги — конкретные действия по пути к цели. В моментах выбора появляются развилки: пользователь принимает решение, и Flow разветвляется — например, авторизован он или нет, выбрал доставку или самовывоз. Финал — точка, в которой цель достигнута и человек получил то, за чем пришёл. Также указываем возможные ошибки и корнер-кейсы.&lt;p&gt;&lt;strong&gt;Как это работает на практике&lt;/strong&gt;&lt;p&gt;Рассмотрим на примере нашего проекта «Самолёт Сити», геймификация онбординга сотрудников. Клиент обозначил 7 главных задач пользователя: последовательное прохождение материалов, возможность повторного просмотра, рейтинг, ачивки, техподдержка и мини-игры.&lt;p&gt;Работали с каждой задачей отдельно, продумывали конечный результат и путь к нему. Затем искали точки пересечения. Например, повторный просмотр материалов должен быть доступен в один клик от стартового экрана — значит, это самостоятельный раздел. При этом он контекстуально связан с последовательным прохождением, значит, их состояния влияют друг на друга и дизайн можно переиспользовать. То же самое с рейтингом и ачивками, лучше объединить в одном разделе.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/900/918/f84/900918f8433d711f53f55536141c8e07.png alt=&#34;Пример User Flow для «Самолёт Сити»&#34; title=&#34;Пример User Flow для «Самолёт Сити»&#34; width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/900/918/f84/900918f8433d711f53f55536141c8e07.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/900/918/f84/900918f8433d711f53f55536141c8e07.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Пример User Flow для «Самолёт Сити»&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Четыре признака хорошего User Flow:&lt;/strong&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Важные функции доступны в один клик от стартового экрана.Запуск онбординга, повтор обучающих материалов, просмотр прогресса.&lt;li&gt;&lt;p&gt;Задачи с общим контекстом объединены в один Flow. Просмотр ачивок и своего места в турнирной таблице.&lt;li&gt;&lt;p&gt;Альтернативные пути предусмотрены и не ведут в тупик. Если пользователь забыл пароль или ввел неверные данные, то Flow подсказывает, что делать дальше, а не просто выдает ошибку.&lt;li&gt;&lt;p&gt;Flow заканчивается ощущением завершенности. После оплаты или другого конечнго действия пользователь видит подтверждение и понимает, что цель достигнута – заказ оформлен, файл сохранен, задача создана.&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;Как построить User Flow&lt;/strong&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/4be/118/30e/4be11830e63f46ee9dcdd435eaa1759d.png width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/4be/118/30e/4be11830e63f46ee9dcdd435eaa1759d.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/4be/118/30e/4be11830e63f46ee9dcdd435eaa1759d.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h2&gt;4. UX-гипотеза: формулируем предположение так, чтобы его можно было проверить&lt;/h2&gt;&lt;p&gt;Когда интерфейс спроектирован, начинается этап отладки. Чтобы избежать принятия решения на основе вкусовщины, формулируются UX-гипотезы.&lt;p&gt;UX-гипотеза — это формализованное предположение о том, как конкретное изменение в интерфейсе повлияет на наблюдаемое поведение пользователя в контексте.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/7bb/bcf/e9e/7bbbcfe9efdb40a98e2075b6983d78ac.png alt=&#34;Формула UX-гипотезы&#34; title=&#34;Формула UX-гипотезы&#34; width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/7bb/bcf/e9e/7bbbcfe9efdb40a98e2075b6983d78ac.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/7bb/bcf/e9e/7bbbcfe9efdb40a98e2075b6983d78ac.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Формула UX-гипотезы&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Как выглядит хорошая гипотеза?&lt;/strong&gt;&lt;p&gt;Хорошая UX-гипотеза должна отвечать «да» на два вопроса.&lt;ol&gt;&lt;li&gt;&lt;p&gt;Понятно ли, что именно меняется и в какой ситуации?&lt;li&gt;&lt;p&gt;Понятно ли, как мы это заметим и на основе чего примем решение?&lt;/ol&gt;&lt;p&gt;Если хотя бы на один ответ «не очень» — гипотеза сырая.&lt;p&gt;Пример&lt;blockquote&gt;&lt;p&gt;Если заменить свободное поле выбора доставки на список с предустановленными вариантами, пользователи, оформляющие заказ впервые, будут реже останавливаться на этом шаге дольше 5 секунд и перестанут возвращаться назад&lt;/blockquote&gt;&lt;p&gt;Такая гипотеза проверяема. В отличие от оценочных суждений без конкретного поведения, вроде «пользователям понравится», «станет понятнее» или «это улучшит UX».&lt;p&gt;&lt;strong&gt;Виды гипотез:&lt;/strong&gt;&lt;p&gt;&lt;code&gt;Поведенческая&lt;/code&gt; — если мы изменим переменную X в сценарии Y, то пользователи сегмента Z начнут или перестанут делать А, что можно зафиксировать через B. Например: если заменить свободное поле выбора доставки на список с предустановленными вариантами, пользователи, оформляющие заказ впервые, будут реже останавливаться на этом шаге более чем 5 секунд и перестанут возвращаться назад.&lt;p&gt;&lt;code&gt;Про ошибки &lt;/code&gt;— станет ли их меньше, фокус не на удобстве, а на исчезновении проблем. Например: если вынести требования к паролю перед полем ввода, пользователи реже будут вводить пароль повторно, что снизит количество ошибок валидации.&lt;p&gt;&lt;code&gt;Про приоритеты внимания&lt;/code&gt; — что пользователь замечает первым и что игнорирует. Например: если основной CTA визуально отделить от вторичных действий, пользователи станут реже выбирать второстепенные сценарии.&lt;p&gt;&lt;code&gt;Про ментальную модель&lt;/code&gt; — совпадают ли пользователя ожидания и структура интерфейса. Например: если сгруппировать настройки по задачам, а не по типам параметров, пользователи будут реже открывать раздел «наугад» и быстрее находить нужную опцию.&lt;p&gt;Через гипотезы мы принимаем более взвешенные решения и минимизируем влияние собственных когнитивных искажений.&lt;h2&gt;Чек-лист перед запуском фичи&lt;/h2&gt;&lt;p&gt;Четыре инструмента, которые мы разобрали, хорошо работают в связке. Вот вопросы, которые стоит задать себе до того, как открывать Figma.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/a0d/d44/647/a0dd4464716d667393e565ee3a0e9618.png alt=&#34;Чек-лист 4 элементов перед запуском продукта&#34; title=&#34;Чек-лист 4 элементов перед запуском продукта&#34; width=2560 height=1440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/a0d/d44/647/a0dd4464716d667393e565ee3a0e9618.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/a0d/d44/647/a0dd4464716d667393e565ee3a0e9618.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Чек-лист 4 элементов перед запуском продукта&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Ни один из этих инструментов не гарантирует идеального результата. Но каждый снижает вероятность того, что вы потратите ресурсы на то, что пользователю не нужно или на то, что он не сможет использовать.&lt;p&gt;&lt;strong&gt;Всем добра!&lt;/strong&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>aareboot</author>
      <guid>https://habr.com/ru/articles/1029804/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029804</guid>
      <pubDate>Thu, 30 Apr 2026 12:00:27 +0000</pubDate>
    </item>
    <item>
      <title>Новый GPU в противовес NVIDIA? Bolt Graphics Zeus</title>
      <link>https://habr.com/ru/companies/selectel/articles/1030116/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030116</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/226/2ea/120/2262ea120d724b84ec1c7b74a0a21ff0.png width=1606 height=824 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/226/2ea/120/2262ea120d724b84ec1c7b74a0a21ff0.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/226/2ea/120/2262ea120d724b84ec1c7b74a0a21ff0.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;22 апреля 2026 года Bolt Graphics &lt;a href=https://www.prnewswire.com/news-releases/bolt-graphics-completes-tape-out-of-test-chip-for-its-high-performance-zeus-gpu-a-major-milestone-in-reducing-computing-costs-by-17x-302750442.html&gt;объявила&lt;/a&gt; об успешном финальном этапе проектирования тестового чипа Zeus на производственных мощностях TSMC. Напомню, это стартап из Калифорнии, основанный в 2020 году. Создатель компании до текущей деятельности занимался проектированием дата-центров и облачной инфраструктуры, но впоследствии переключился на создание GPU для рендеринга.&lt;p&gt;Что это, значимая новость для индустрии или очередной «прожект»? &lt;a href=&#34;https://selectel.ru/services/dedicated/?tab=configuratorGpu&amp;amp;c=385%3A1&amp;amp;simpleRamMode=true&amp;amp;utm_source=habr.com&amp;amp;utm_medium=referral&amp;amp;utm_campaign=dedicated_article_widget_300426_content&#34;&gt;Новые GPU&lt;/a&gt;, имена и конкуренция мэтрам — это всегда хорошо для конечного клиента и двигает рынок вперед. Рассмотрим стадию создания продукта, его технические характеристики и место под солнцем в новостной статье.&lt;h2&gt;Что означает tape-out&lt;/h2&gt;&lt;p&gt;Это момент, когда команда разработчиков чипа отправляет финальную версию дизайна на заводское производство. Буквально: работа над схемой закончена, файлы с топологией уходят на фабрику и изменить уже ничего нельзя. Фабрика запускает производство и спустя время появляются первые образцы — их называют первым кремнием (first silicon) или инженерными семплами.&lt;p&gt;Tape-out — это не готовый продукт и не гарантия работающего чипа. Первый кремний очень часто имеет недоработки, иногда — фатальные, иногда — исправимые. Важно знать, что разрыв между этапом tape-out и коммерческими поставками может доходить до года и даже большего срока.&lt;p&gt;Однако это означает, что компания-разработчик железа уже потратила реальные деньги на производство. Tape-out на TSMC 12nm может стоить несколько миллионов долларов. Можно сказать, что это переход из категории «анонс» в категорию «проект». В случае Bolt Graphics — именно поэтому текущая апрельская новость интереснее всех предыдущих их заявлений. Для них это конкретный технический шаг вперед, но до массового производства еще далеко: компания ориентируется на Q4 2027.&lt;h2&gt;Технические характеристики&lt;/h2&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/360/41a/538/36041a538ba44dcabbc35a372e6eca1b.png width=2036 height=974 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/360/41a/538/36041a538ba44dcabbc35a372e6eca1b.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/360/41a/538/36041a538ba44dcabbc35a372e6eca1b.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Zeus — это несколько моделей, ориентированных на разные сегменты рынка.&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Bolt Zeus 1c26-032&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Bolt Zeus 2c26-064&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Bolt Zeus 2x26-128&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Bolt Zeus 4c26-256&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Форм-фактор&lt;td&gt;&lt;p align=left&gt;Однослотовая PCIe&lt;td&gt;&lt;p align=left&gt;Двухслотовая PCIe&lt;td&gt;&lt;p align=left&gt;Двухслотовая PCIe&lt;td&gt;&lt;p align=left&gt;Двухслотовая PCIe&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Мощность&lt;td&gt;&lt;p align=left&gt;120 Вт&lt;td&gt;&lt;p align=left&gt;250 Вт&lt;td&gt;&lt;p align=left&gt;250 Вт&lt;td&gt;&lt;p align=left&gt;500 Вт&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;FP64/FP32/FP16 TFLOPS&lt;td&gt;&lt;p align=left&gt;5/10/20&lt;td&gt;&lt;p align=left&gt;10/20/40&lt;td&gt;&lt;p align=left&gt;10/20/40&lt;td&gt;&lt;p align=left&gt;20/40/80&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;INT16/INT8 TFLOPS&lt;td&gt;&lt;p align=left&gt;307,2 / 614,4&lt;td&gt;&lt;p align=left&gt;614,4 / 1 228,8&lt;td&gt;&lt;p align=left&gt;614,4 / 1 228,8&lt;td&gt;&lt;p align=left&gt;1 228,8 / 2 457,6&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Кеш&lt;td&gt;&lt;p align=left&gt;128 МБ&lt;td&gt;&lt;p align=left&gt;256 МБ&lt;td&gt;&lt;p align=left&gt;256 МБ&lt;td&gt;&lt;p align=left&gt;512 МБ&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Память&lt;td&gt;&lt;p align=left&gt;32 ГБ LPDDR5X&lt;td&gt;&lt;p align=left&gt;64 ГБ LPDDR5X&lt;td&gt;&lt;p align=left&gt;128 ГБ LPDDR5X&lt;td&gt;&lt;p align=left&gt;256 ГБ LPDDR5X&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Расширение памяти&lt;td&gt;&lt;p align=left&gt;до 160 ГБ, два слота DDR5 SO-DIMM&lt;td&gt;&lt;p align=left&gt;до 320 ГБ, четыре слота DDR5 SO-DIMM&lt;td&gt;&lt;p align=left&gt;до 384 ГБ, четыре слота DDR5 SO-DIMM&lt;td&gt;&lt;p align=left&gt;до 2304 ГБ, восемь слотов DDR5 SO-DIMM&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Сетевые интерфейсы&lt;td&gt;&lt;p align=left&gt;400 GbE (QSFP-DD)&lt;td&gt;&lt;p align=left&gt;400 GbE (QSFP-DD)&lt;td&gt;&lt;p align=left&gt;400 GbE (QSFP-DD)&lt;td&gt;&lt;p align=left&gt;6x 800 GbE (OSFP)&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Вместо распространенной в индустрии GDDR-памяти Zeus использует LPDDR5X плюс расширяемые DDR5 SO-DIMM — это дешевле, чем GDDR7 или HBM3e. Но все еще позволяет иметь большой объем до 384 ГБ. Однако пропускная способность памяти оценивается не более 300 ГБ/сек. Тогда как в GDDR7 это показатель в несколько раз выше.&lt;p&gt;В архитектуре заявлены встроенные ядра RISC-V, которые берут на себя управление пересечениями лучей с треугольниками — это главная вычислительная операция в трассировке путей. Вместо того чтобы гонять эту задачу через шейдерные ALU, Bolt предлагает специализированные аппаратные блоки. Насколько это эффективно на практике — покажут только независимые бенчмарки на реальном железе.&lt;p&gt;Еще одна нетривиальная особенность: &lt;a href=https://bolt.graphics/wp-content/uploads/2025/10/Bolt-Zeus-Announcement.pdf&gt;заявлены&lt;/a&gt; встроенные интерфейсы 400GbE и 800GbE для прямого соединения GPU без InfiniBand. Это потенциально интересно для кластеров рендер-ферм и HPC, где стоимость сетевой инфраструктуры — существенная статья расходов.&lt;p&gt;Что касается техпроцесса: тестовый чип изготовлен на 12nm FFC TSMC. Компания упоминает, что архитектура масштабируется на 5nm, но продакшен-версия пока привязана к 12nm. Для справки: RTX 4090 выпускалась на N5P (4nm class), RTX 5090 — на N4P. Это означает, что тактовые частоты и плотность транзисторов у Zeus будут скромнее.&lt;h2&gt;Конкуренты&lt;/h2&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d47/e63/5a3/d47e635a3be2c100ba3054cf52b09926.png width=2048 height=812 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d47/e63/5a3/d47e635a3be2c100ba3054cf52b09926.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d47/e63/5a3/d47e635a3be2c100ba3054cf52b09926.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Bolt Graphics сравнивает Zeus в основном с RTX 5090. В позиционировании NVIDIA это консьюмерская карта, а не профессиональные ускорители вроде H200 или MI350X.&lt;p&gt;&lt;strong&gt;По заявлениям компании&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Zeus 1c26 (120 Вт) vs RTX 5090 (575 Вт) — примерно в 2,5 раза быстрее в трассировке путей;&lt;li&gt;&lt;p&gt;Zeus 2c26 (250 Вт) vs RTX 5090 — в 5 раз быстрее в трассировке путей;&lt;li&gt;&lt;p&gt;Zeus 4c vs RTX 5090 — до 10–13 раз быстрее в трассировке, в 6 раз в HPC-задачах, и совершенно фантастические 300x в симуляциях ЭМ-волн.&lt;/ul&gt;&lt;p&gt;Все эти цифры получены из «pre-silicon benchmarks in emulation» — то есть из симуляций на FPGA, а не с реального кремния. Это важная оговорка.&lt;div class=floating-image&gt;&lt;/div&gt;&lt;div class=floating-image&gt;&lt;figure class=float&gt;&lt;img src=https://habrastorage.org/getpro/habr/upload_files/25f/5de/847/25f5de8473175bbc297208a7f69590df.gif width=220 height=220 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/getpro/habr/upload_files/25f/5de/847/25f5de8473175bbc297208a7f69590df.gif 780w,&#xA;       https://habrastorage.org/getpro/habr/upload_files/25f/5de/847/25f5de8473175bbc297208a7f69590df.gif 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Арендуйте GPU за 1 рубль!&lt;/strong&gt;&lt;p&gt;Выберите нужную конфигурацию в панели управления Selectel. &lt;abbr class=habraabbr title=&#34;При заказе сервера произвольной конфигурации в Selectel в рамках Акции при выборе GPU Tesla T4, GPU Nvidia A2 либо A2000. Количество доступных для заказа в рамках Акции GPU-карт ограничено.&#34; data-title=&#34;&amp;lt;p&amp;gt;При заказе сервера произвольной конфигурации в Selectel в рамках Акции при выборе GPU Tesla T4, GPU Nvidia A2 либо A2000. Количество доступных для заказа в рамках Акции GPU-карт ограничено.&amp;lt;/p&amp;gt;&amp;lt;p&amp;gt;&amp;lt;/p&amp;gt;&#34; data-abbr=*&gt;*&lt;/abbr&gt;&lt;p&gt;&lt;a href=&#34;https://selectel.ru/services/dedicated/?tab=configuratorGpu&amp;amp;c=385%3A1&amp;amp;simpleRamMode=true&amp;amp;utm_source=habr.com&amp;amp;utm_medium=referral&amp;amp;utm_campaign=dedicated_article_widget_300426_banner_i089_01_ord&#34;&gt;&lt;strong&gt;Подробнее →&lt;/strong&gt;&lt;/a&gt;&lt;/div&gt;&lt;h2&gt;«А что с инференсом?»&lt;/h2&gt;&lt;p&gt;Самая интересная и одновременно самая проблемная часть истории Zeus обнаруживается в инференсе.&lt;p&gt;Инференс LLM — сегодня крупнейший и наиболее платежеспособный сегмент спроса на ускорители. «Тяжелый люкс» и уже стандарт индустрии здесь — это NVIDIA B300, NVIDIA H200 или хотя бы NVIDIA RTX PRO 6000 Blackwell&lt;p&gt;Zeus в этом контексте выглядит неоднозначно. С одной стороны, возможность иметь 128 ГБ LPDDR5X на двухчиплетной или 256 ГБ LPDDR5X на четырехчиплетной карте — это потенциально интересно для инференса с большими моделей даже без квантизации. С другой стороны, LPDDR5X дает принципиально меньшую пропускную способность, чем HBM и даже GDDR7. А этот параметр принципиальный для инференса.&lt;p&gt;Еще одна проблема для инференса — это не железо, а программный стек. PyTorch, vLLM, TensorRT-LLM, FlashAttention — не пустые слова, весь современный инференс-стек заточен под CUDA или хотя бы ROCm. Bolt Graphics предлагает собственный рендерер Glowstick и API на базе RISC-V, но ни слова про поддержку стандартных ML-фреймворков в публичных материалах нет. Без этого Zeus для инференса слабоприменим вне зависимости от характеристик железа.&lt;p&gt;Сама компания в своих материалах честно и не позиционирует Zeus как ускоритель для инференса, что означает отсутствие предложения в самом горячем сегменте рынка.&lt;h2&gt;Состоится ли Zeus&lt;/h2&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d17/fce/f14/d17fcef14500957479ab78916c27f075.png width=1856 height=714 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d17/fce/f14/d17fcef14500957479ab78916c27f075.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d17/fce/f14/d17fcef14500957479ab78916c27f075.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;В 2023 году на CES Bolt Graphics анонсировала первые продукты: мобильный GPU Lightning и десктопные GPU Thunder. Компания заявляла о высокой производительности и сравнивала устройства с NVIDIA RTX 4090. &lt;p&gt;На CES 2024 Thunder был анонсирован повторно — уже «latest edition» (самая новая версия). Компания также обещала прототипные чипы на 5nm с поставками в конце 2024 года.&lt;p&gt;В реальности: ни один из этих продуктов не появился в коммерческой продаже. Нет публичных обзоров, нет независимых бенчмарков. Ни один публичный источник не сообщает о реально поставленных чипах Lightning или Thunder кому-либо из заказчиков. На сайте компании эти продукты также сложно найти.&lt;p&gt;Итак, сейчас анонсирован tape-out тестового чипа на 12nm. Это уже конкретный результат, которого у предыдущих продуктов не было публично. Девкиты планируются на конец 2026 года, массовое производство — на 2027 год.&lt;p&gt;Tape-out на TSMC — это не просто пресс-релиз, ведь потрачены реальные деньги. Значит, инвесторы продолжают вкладываться. Архитектурная ставка на специализированные блоки трассировки лучей теоретически обоснована — NVIDIA сама отчасти движется в эту сторону. Зарубежный рынок рендер-ферм, VFX-студий и HPC действительно платит много и реально ограничен в доступе к compute, в частности — на фоне конкуренции с задачами инференса.&lt;p&gt;Но история компании не вселяет уверенности. Сроки постоянно сдвигаются: в начале 2025 года компания обещала девкиты в 2025, сейчас это конец 2026, при этом производство сдвинулось с 2026 на 2027 год. Все цифры производительности по-прежнему основаны на эмуляции, а не на реальном кремнии.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ea4/e94/f2a/ea4e94f2aee4b39c1fc752a7c1c232f1.png width=1450 height=1066 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ea4/e94/f2a/ea4e94f2aee4b39c1fc752a7c1c232f1.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ea4/e94/f2a/ea4e94f2aee4b39c1fc752a7c1c232f1.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Отдельная проблема — программный стек. NVIDIA потратила 15 лет на построение CUDA-экосистемы, AMD тратит годы на ROCm и все еще догоняет. У Bolt Graphics менее ста сотрудников, собственный рендерер (не путать с «рендер») Glowstick и API на базе RISC-V. Для нишевого применения в рендеринге этого может хватить, для HPC — уже сомнительно, для инференса — утопия.&lt;p&gt;И конкуренты, мягко говоря, не стоят на месте и вообще не собираются останавливаться. К 2027 Nvidia будет как минимум с GPU Rubin, максимум — выпустит следующее поколение. AMD перевалит в индексах GPU MI за цифру 400. Рынок, в который Zeus хочет войти, к тому моменту будет другим.&lt;p&gt;Реалистичный сценарий: если Zeus доберется до рынка, он найдет нишу в специализированных рендер-фермах и конкретных HPC-задачах вроде ЭМ-симуляций — там, где важны огромные объемы памяти и специфические паттерны вычислений. Еще одним важнейшим фактором будет цена изделий и соотношение с их производительностью. Очевидно, что получить более выгодную цену на масштабах производства миллионов GPU у конкурентов стартапа значительно проще, чем у новичка на несколько десятков тысяч.&lt;p&gt;Стать широким конкурентом для NVIDIA в инференсе или общем HPC без серьезного инвестирования в программный стек и масштабы производства точно не получится. Общий GPU-рынок никаких инъекций, к сожалению не почувствует.&lt;blockquote&gt;&lt;p&gt;Однако мы продолжаем следить за новинками рынка в надежде, что когда-нибудь у NVIDIA и AMD появится достойный конкурент, а Selectel напишет, что знали про них еще на заре появления первых устройств.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/selectel/articles/1030116/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030116</guid>
      <pubDate>Thu, 30 Apr 2026 12:00:04 +0000</pubDate>
    </item>
    <item>
      <title>[Перевод] Стратегия резервного копирования 3-2-1: технический разбор</title>
      <link>https://habr.com/ru/companies/cloud4y/articles/1030110/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030110</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Целостность данных — краеугольный камень современной корпоративной инфраструктуры. Базовых сценариев резервного копирования может хватать дома, но в корпоративной среде нужна жёсткая, отказоустойчивая методология. Стратегия 3-2-1 по-прежнему остаётся индустриальным стандартом, обеспечивая устойчивость данных к повреждениям, аппаратным сбоям и атакам шифровальщиков. Эта статья разбирает техническую реализацию правила 3-2-1 — в первую очередь для системных администраторов и архитекторов, проектирующих решения с высокой доступностью.&lt;h3&gt;Разбираем правило 3-2-1 по частям&lt;/h3&gt;&lt;p&gt;В своей основе 3-2-1 — это протокол избыточности, спроектированный так, чтобы устранить единые точки отказа (SPOF) в системе хранения.&lt;h4&gt;Три копии данных&lt;/h4&gt;&lt;p&gt;Правило требует поддерживать как минимум три копии любого набора данных: продакшн-данные плюс две независимые резервные копии. Статистически такая избыточность серьёзно снижает вероятность одновременной потери. Если вероятность отказа одного диска равна 1/100, то вероятность одновременного отказа трёх независимых дисков падает до 1/1 000 000 — при условии, что отказы не коррелируют между собой.&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Прим. перев.&lt;/strong&gt; Оговорка про «независимые» отказы здесь принципиальная. На практике диски из одной партии с одной прошивкой, лежащие в одной полке, в одной стойке, в одном ЦОД — это не три независимых события. Поэтому 1/1 000 000 — это идеализированная нижняя граница, а не цифра, которую вы реально получите в продакшене. Ровно поэтому пункты про «разные носители» и «другая локация» в правиле не менее важны, чем сам факт трёх копий.&lt;/blockquote&gt;&lt;h4&gt;Два разных типа носителей&lt;/h4&gt;&lt;p&gt;Две из копий должны лежать на разных типах носителей. Это требование снижает риск проблем, специфичных для конкретного носителя — например, багов прошивки в конкретной партии HDD или деградации оптических носителей. Использование разнородных технологий хранения — скажем, связки массивов на «крутящихся» дисках (HDD) с SSD или магнитной лентой — отвязывает резервные копии от уязвимостей конкретной технологии.&lt;h4&gt;Одна копия за пределами площадки&lt;/h4&gt;&lt;p&gt;Последняя копия должна находиться географически отдельно от продакшн-среды. Это защищает от катастроф уровня площадки: пожара, потопа или физической кражи. В современных архитектурах под «offsite» чаще всего понимают неизменяемое (immutable) облачное хранилище или физически отдельный ЦОД, подключённый по WAN.&lt;h3&gt;Стратегии реализации в продвинутых архитектурах&lt;/h3&gt;&lt;p&gt;Для энтерпрайз-уровня простого копирования файлов мало. Продвинутая реализация 3-2-1 требует автоматизации, верификации и интеграции с системами безопасности.&lt;h4&gt;Оркестрация трёх копий&lt;/h4&gt;&lt;p&gt;Серьёзные реализации опираются на технологии снапшотов и репликации, а не на банальное копирование файлов.&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Продакшн-данные.&lt;/strong&gt; Лежат на высокопроизводительных массивах SAN/NAS.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Первичный бэкап.&lt;/strong&gt; Частые инкрементальные снапшоты, хранящиеся на отдельной локальной системе. Это даёт низкое время восстановления (RTO) для повседневных задач восстановления.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Вторичный бэкап.&lt;/strong&gt; Асинхронная репликация наборов резервных копий в третью локацию.&lt;/ul&gt;&lt;h4&gt;Разнообразие носителей в эпоху Software-Defined&lt;/h4&gt;&lt;p&gt;В среде программно-определяемых хранилищ (SDS) различия физических носителей могут быть абстрагированы. Однако настоящее соответствие правилу требует физически разного «железа» под капотом.&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disk-to-Disk-to-Tape (D2D2T).&lt;/strong&gt; Некоторые считают это легаси, но LTO-лента остаётся жизнеспособным носителем для архивного хранения с воздушным зазором (air-gapped). Высокая плотность записи плюс защита от горизонтального распространения шифровальщиков — не самые плохие свойства.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Disk-to-Disk-to-Cloud (D2D2C).&lt;/strong&gt; Использование объектного хранилища (S3-совместимого) в качестве второго носителя. Чтобы получить честное различие носителей, локальный репозиторий может работать с блочным хранилищем, а облачный — с объектным.&lt;/ul&gt;&lt;h4&gt;Защита offsite-копии&lt;/h4&gt;&lt;p&gt;Offsite-компонент — ключевая линия обороны против шифровальщиков.&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Неизменяемость (Immutability).&lt;/strong&gt; Настройка политик Object Lock (WORM — Write Once Read Many) для облачных репозиториев. Это блокирует любому пользователю, включая администраторов, возможность удалить или изменить блоки бэкапа до истечения срока хранения.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Воздушный зазор (Air-Gapping).&lt;/strong&gt; Для повышенной безопасности применяется логический или физический air gap, при котором сеть управления резервным устройством не имеет постоянного подключения к продакшн-сети или интернету.&lt;/ul&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;От Cloud4Y.&lt;/strong&gt; Если задумываетесь, где разместить ту самую «третью копию за периметром», у нас сейчас как раз идёт акция для читателей Хабра: новым клиентам — скидка 20% на аренду облачных серверов для бизнеса по промокоду &lt;strong&gt;HABR20&lt;/strong&gt;. Серверы размещаются в ЦОД уровня TIER III, SLA — 99,982%, можно взять готовую конфигурацию или собрать машину под задачу. Условия — на &lt;a href=&#34;https://www.cloud4y.ru/promo/skidka-20-habr/?utm_source=habr&amp;amp;utm_medium=article&amp;amp;utm_campaign=discount&amp;amp;utm_content=link&amp;amp;utm_term=321backup&#34;&gt;странице акции&lt;/a&gt;, перейдите по &lt;a href=&#34;https://www.cloud4y.ru/promo/skidka-20-habr/?utm_source=habr&amp;amp;utm_medium=article&amp;amp;utm_campaign=discount&amp;amp;utm_content=link&amp;amp;utm_term=321backup&#34;&gt;ссылке&lt;/a&gt;. На арендованном сервере (или связке из нескольких) удобно поднять свой бэкап-таргет под Veeam, Commvault, Acronis или любой другой привычный инструмент — и спокойно проверить схему 3-2-1 на реальных данных, прежде чем закладывать её в основную архитектуру.&lt;/blockquote&gt;&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Прим. перев.&lt;/strong&gt; К теме air gap: за последние пару лет термин в индустрии заметно девальвировался — его стали лепить на любые решения, у которых бэкап-таргет «обычно не подключён к сети». Настоящий air gap означает, что хранилище недостижимо ни сетево, ни через identity, ни через API, ни через какой бы то ни было автоматизированный процесс — в момент инцидента. Если ваш «air-gapped» бэкап доступен оттуда же, откуда скомпрометированная среда, — это галочка для аудита, а не защита.&lt;/blockquote&gt;&lt;h3&gt;Плюсы и минусы модели 3-2-1&lt;/h3&gt;&lt;p&gt;Модель надёжна, но в корпоративной среде у неё есть свои компромиссы.&lt;p&gt;&lt;strong&gt;Плюсы:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Устойчивость.&lt;/strong&gt; Максимизирует выживаемость данных при разных доменах отказа.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Универсальность.&lt;/strong&gt; Не привязана к конкретным вендорам железа или ПО для бэкапа.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Соответствие требованиям.&lt;/strong&gt; Закрывает требования к восстановлению после аварий в большинстве регуляторных рамок (GDPR, HIPAA, SOC 2).&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Минусы:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Стоимость.&lt;/strong&gt; Содержание избыточного железа и плата за исходящий трафик из облака увеличивают совокупную стоимость владения (TCO).&lt;li&gt;&lt;p&gt;&lt;strong&gt;Сложность.&lt;/strong&gt; Синхронизация по разнородным носителям и локациям требует серьёзных инструментов оркестрации.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Задержки.&lt;/strong&gt; Восстановление больших наборов данных из удалённого «холодного» хранилища может ухудшать RTO по сравнению с локальным восстановлением.&lt;/ul&gt;&lt;h3&gt;Инженерия отказоустойчивости&lt;/h3&gt;&lt;p&gt;Стратегия 3-2-1 — не просто рекомендация, а фундаментальное архитектурное требование к долговечности данных. За счёт избыточности по числу копий, типам носителей и географии организации эффективно изолируют себя от катастрофической потери данных. Для опытного практика успех заключается не в самой концепции, а в её строгом применении: неизменяемое хранилище, air gap и автоматическая верификация.&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Прим. перев.&lt;/strong&gt; Канонические 3-2-1 за последние годы расширили до 3-2-1-1-0 (одна неизменяемая копия + ноль ошибок при восстановлении — версия Veeam) и 4-3-2 — именно потому, что классические три копии всё хуже справляются с современными атаками, где злоумышленник целенаправленно идёт за бэкапами. Различия между этими схемами заслуживают отдельного материала — постараемся его сделать.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/cloud4y/articles/1030110/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030110</guid>
      <pubDate>Thu, 30 Apr 2026 11:48:49 +0000</pubDate>
    </item>
    <item>
      <title>Как я завалил кучу собесов по Go и что из этого вынес</title>
      <link>https://habr.com/ru/articles/1030108/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030108</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;&lt;em&gt;Привет! Меня зовут Александр. Некоторые могут помнить мои статьи про финансовую аналитику на Python - анализ ETF, оптимизацию портфелей. Но последние 6 лет я Senior Go Backend Engineer, специализируюсь на финтехе и трейдинге.&lt;/em&gt;&lt;p&gt;&lt;em&gt;Эта комбинация - domain expertise в финансах + техническая экспертиза в Go - оказалась очень ценной. Но путь был тернистым.&lt;/em&gt;&lt;p&gt;&lt;em&gt;Последние полгода активно собеседовался: 8 интервью в разных компаниях - от крупных российских IT-гигантов до международных финтех стартапов. Где-то взяли, где-то нет. Решил поделиться самыми дурацкими ошибками, которые делал я и другие кандидаты. Может, кому поможет не наступить на те же грабли.&lt;/em&gt;&lt;h3&gt;Немного предыстории&lt;/h3&gt;&lt;p&gt;В начале прошлого года решил поменять работу. Думаю - ну что там сложного, Go знаю, опыта хватает, пойду поумничаю на интервью. Ага, щас! Первый же собес в крупной компании завалил так, что до сих пор стыдно.&lt;p&gt;Но обо всем по порядку. Вот топ-10 косяков, которые я либо сам делал, либо видел у других кандидатов.&lt;hr&gt;&lt;h3&gt;1. “Что выведет этот код?” - и тут началось…&lt;/h3&gt;&lt;p&gt;Боже, сколько раз я на этом прокалывался! Особенно запомнился интервью в одной крупной финтех компании (международная, специализируется на трейдинге).&lt;p&gt;Дают мне код:&lt;pre&gt;&lt;code class=go&gt;func main() {&#xA;    s := make([]int, 0, 5)&#xA;    s = append(s, 1, 2, 3)&#xA;    &#xA;    a := append(s, 4)&#xA;    b := append(s, 5)&#xA;    &#xA;    fmt.Println(a) &#xA;    fmt.Println(b) &#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Я, умный такой, отвечаю: “Ну понятно же - &lt;code&gt;a&lt;/code&gt; будет &lt;code&gt;[1,2,3,4]&lt;/code&gt;, а &lt;code&gt;b&lt;/code&gt; будет &lt;code&gt;[1,2,3,5]&lt;/code&gt;”.&lt;p&gt;Интервьюер так грустно на меня посмотрел… Оказывается, оба слайса будут &lt;code&gt;[1,2,3,5]&lt;/code&gt;!&lt;p&gt;Почему? Да потому что underlying array у них общий, capacity хватает, и когда мы делаем &lt;code&gt;append(s, 5)&lt;/code&gt;, это затирает четверку в том же массиве.&lt;p&gt;Я тогда честно признался: “Не, ну это я не знал”. Интервьюер говорит: “А как же вы баги в проде ловите?” А я думаю: “Ну… методом тыка?” Конечно, вслух не сказал, но по лицу было видно.&lt;p&gt;&lt;strong&gt;Мораль:&lt;/strong&gt; Надо знать, как слайсы устроены под капотом. Не просто “это динамический массив”, а реально понимать про capacity и underlying array.&lt;hr&gt;&lt;h3&gt;2. Race conditions - “Да что тут такого сложного?”&lt;/h3&gt;&lt;p&gt;Это вообще отдельная песня. В одной криптоплатежной компании (европейская, миллиарды в обороте) дали задачку написать простенький счетчик:&lt;pre&gt;&lt;code class=go&gt;type Counter struct {&#xA;    value int&#xA;}&#xA;&#xA;func (c *Counter) Increment() {&#xA;    c.value++ // Чего тут сложного-то?&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Я говорю: “Ну все, готово”. А интервьюер: “А что будет, если много горутин будут это вызывать?”&lt;p&gt;И тут я понял, что проштрафился. &lt;code&gt;c.value++&lt;/code&gt; - это не одна операция, а три: прочитать, увеличить, записать. А между ними другая горутина может влезть.&lt;p&gt;Правильно было бы так:&lt;pre&gt;&lt;code class=go&gt;type Counter struct {&#xA;    mu    sync.Mutex&#xA;    value int&#xA;}&#xA;&#xA;func (c *Counter) Increment() {&#xA;    c.mu.Lock()&#xA;    defer c.mu.Unlock()&#xA;    c.value++&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Или еще проще - через atomic:&lt;pre&gt;&lt;code class=go&gt;type Counter struct {&#xA;    value int64&#xA;}&#xA;&#xA;func (c *Counter) Increment() {&#xA;    atomic.AddInt64(&amp;amp;c.value, 1)&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Фишка в том&lt;/strong&gt;, что надо понимать КОГДА что использовать. Для простого счетчика - atomic. Для сложной логики - mutex. Для передачи данных между горутинами - channels.&lt;p&gt;Кстати, один интервьюер мне формулу дал: “atomic → RWMutex → Mutex → channel”. По возрастанию сложности и накладных расходов.&lt;hr&gt;&lt;h3&gt;3. PostgreSQL и деньги - тут шутки плохи&lt;/h3&gt;&lt;p&gt;А вот это был реально стыдный момент. В той же финтех компании дают задачу: “Напишите функцию перевода денег между счетами”.&lt;p&gt;Я, наивный, пишу:&lt;pre&gt;&lt;code class=go&gt;func TransferMoney(from, to int, amount decimal.Decimal) error {&#xA;    tx, err := db.Begin()&#xA;    // ... проверки ошибок&#xA;    &#xA;    var balance decimal.Decimal&#xA;    err = tx.QueryRow(&amp;#34;SELECT balance FROM accounts WHERE id = ?&amp;#34;, from).Scan(&amp;amp;balance)&#xA;    &#xA;    if balance.LessThan(amount) {&#xA;        return errors.New(&amp;#34;денег нет, но вы держитесь&amp;#34;)&#xA;    }&#xA;    &#xA;    // Списываем&#xA;    tx.Exec(&amp;#34;UPDATE accounts SET balance = balance - ? WHERE id = ?&amp;#34;, amount, from)&#xA;    // Зачисляем&#xA;    tx.Exec(&amp;#34;UPDATE accounts SET balance = balance + ? WHERE id = ?&amp;#34;, amount, to)&#xA;    &#xA;    return tx.Commit()&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Интервьюер спрашивает: “А что будет, если две транзакции одновременно переводят деньги с одного счета?”&lt;p&gt;Я думаю: “Ну, транзакция же, изоляция…” А он говорит: “А между SELECT и UPDATE другая транзакция может успеть?”&lt;p&gt;И правда может! Классический Lost Update. Надо было блокировать строку:&lt;pre&gt;&lt;code class=go&gt;err = tx.QueryRow(&amp;#34;SELECT balance FROM accounts WHERE id = ? FOR UPDATE&amp;#34;, from).Scan(&amp;amp;balance)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Этот &lt;code&gt;FOR UPDATE&lt;/code&gt; блокирует строку до конца транзакции.&lt;p&gt;&lt;strong&gt;Урок:&lt;/strong&gt; В финтехе шутки плохи. Не знаешь &lt;code&gt;SELECT FOR UPDATE&lt;/code&gt; - не работай с деньгами.&lt;hr&gt;&lt;h3&gt;4. Context - “А зачем он мне?”&lt;/h3&gt;&lt;p&gt;Помню собес в одной большой российской IT-компании. Дают задачку написать функцию, которая ходит в API. Я пишу:&lt;pre&gt;&lt;code class=go&gt;func GetUser(userID int) (*User, error) {&#xA;    resp, err := http.Get(fmt.Sprintf(&amp;#34;http://api/users/%d&amp;#34;, userID))&#xA;    // дальше парсинг...&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;“А context где?” - спрашивает интервьюер.&lt;p&gt;“А зачем он тут? Это же просто GET запрос” - отвечаю я.&lt;p&gt;Оказывается, зачем:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Timeout control - а то запрос может висеть вечно&lt;li&gt;&lt;p&gt;Cancellation - если пользователь ушел, зачем тратить ресурсы?&lt;li&gt;&lt;p&gt;Trace propagation - для мониторинга&lt;/ol&gt;&lt;p&gt;Правильно:&lt;pre&gt;&lt;code class=go&gt;func GetUser(ctx context.Context, userID int) (*User, error) {&#xA;    req, err := http.NewRequestWithContext(ctx, &amp;#34;GET&amp;#34;, &#xA;        fmt.Sprintf(&amp;#34;http://api/users/%d&amp;#34;, userID), nil)&#xA;    &#xA;    client := &amp;amp;http.Client{Timeout: 5 * time.Second}&#xA;    resp, err := client.Do(req)&#xA;    // ...&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Правило:&lt;/strong&gt; Context - первый параметр в любой функции, которая может “повиснуть”.&lt;hr&gt;&lt;h3&gt;5. Горутины и утечки памяти&lt;/h3&gt;&lt;p&gt;Это я понял не сразу. На одном собесе дают задачу: “Обработайте массив данных параллельно”.&lt;p&gt;Я, как настоящий гений, пишу:&lt;pre&gt;&lt;code class=go&gt;func ProcessData(data []string) {&#xA;    for _, item := range data {&#xA;        go func(item string) {&#xA;            result := heavyProcessing(item)&#xA;            fmt.Println(result) // И кто это будет читать?&#xA;        }(item)&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;“А как вы узнаете, что все обработалось?” - спрашивает интервьюер.&lt;p&gt;“Ну… подождем?” - говорю я.&lt;p&gt;“Сколько?”&lt;p&gt;“Ну… достаточно?”&lt;p&gt;Конечно, это неправильно. Горутины запустятся и “уплывут”. В production это memory leak.&lt;p&gt;Правильно - через WaitGroup:&lt;pre&gt;&lt;code class=go&gt;func ProcessData(data []string) error {&#xA;    var wg sync.WaitGroup&#xA;    errCh := make(chan error, len(data))&#xA;    &#xA;    for _, item := range data {&#xA;        wg.Add(1)&#xA;        go func(item string) {&#xA;            defer wg.Done()&#xA;            &#xA;            if err := heavyProcessing(item); err != nil {&#xA;                errCh &amp;lt;- err&#xA;            }&#xA;        }(item)&#xA;    }&#xA;    &#xA;    go func() {&#xA;        wg.Wait()&#xA;        close(errCh)&#xA;    }()&#xA;    &#xA;    for err := range errCh {&#xA;        if err != nil {&#xA;            return err&#xA;        }&#xA;    }&#xA;    return nil&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Фишка:&lt;/strong&gt; Всегда знай, когда твои горутины закончатся. И как ловить их ошибки.&lt;hr&gt;&lt;h3&gt;6. interface{} - универсальное зло&lt;/h3&gt;&lt;p&gt;Ох, сколько я на этом налетел! Думал - раз Go такой строгий с типами, то &lt;code&gt;interface{}&lt;/code&gt; - это способ “расслабиться”.&lt;pre&gt;&lt;code class=go&gt;func Process(data interface{}) interface{} {&#xA;    switch v := data.(type) {&#xA;    case string:&#xA;        return strings.ToUpper(v)&#xA;    case int:&#xA;        return v * 2&#xA;    case []interface{}:&#xA;        // тут начинается рекурсивный ад...&#xA;    default:&#xA;        return &amp;#34;хз что это&amp;#34;&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Интервьюер говорит: “А что будет, если я передам &lt;code&gt;map[string]int&lt;/code&gt;?”&lt;p&gt;Я: “Ну… вернется ‘хз что это’”&lt;p&gt;“А вы узнаете об этом когда?”&lt;p&gt;“Ну… в runtime?”&lt;p&gt;“Вот именно. А можно узнать раньше?”&lt;p&gt;Оказывается, можно. Либо через generics (если Go 1.18+), либо просто нормальными типами:&lt;pre&gt;&lt;code class=go&gt;type UserRequest struct {&#xA;    Name string `json:&amp;#34;name&amp;#34;`&#xA;}&#xA;&#xA;func ProcessUser(req UserRequest) (*UserResponse, error) {&#xA;    // Тут все понятно и type-safe&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Правило:&lt;/strong&gt; &lt;code&gt;interface{}&lt;/code&gt; только когда РЕАЛЬНО нужен any type. А это редко.&lt;hr&gt;&lt;h3&gt;7. Ошибки - “Залогировал и забыл”&lt;/h3&gt;&lt;p&gt;Еще один мой косяк. Пишу функцию:&lt;pre&gt;&lt;code class=go&gt;func GetUser(id int) *User {&#xA;    user, err := db.GetUser(id)&#xA;    if err != nil {&#xA;        log.Printf(&amp;#34;Ошибка: %v&amp;#34;, err) // &amp;#34;Залогировал же!&amp;#34;&#xA;        return nil&#xA;    }&#xA;    return user&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Интервьюер: “А как вызывающий код поймет, что произошло?”&lt;p&gt;Я: “Ну… &lt;code&gt;nil&lt;/code&gt; же вернется”&lt;p&gt;“А &lt;code&gt;nil&lt;/code&gt; может быть и потому что пользователя нет, и потому что БД упала?”&lt;p&gt;Ага, точно. Caller не поймет, что делать с &lt;code&gt;nil&lt;/code&gt;.&lt;p&gt;Правильно:&lt;pre&gt;&lt;code class=go&gt;func GetUser(id int) (*User, error) {&#xA;    user, err := db.GetUser(id)&#xA;    if err != nil {&#xA;        return nil, fmt.Errorf(&amp;#34;не смог достать пользователя %d: %w&amp;#34;, id, err)&#xA;    }&#xA;    return user, nil&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Фишка в &lt;/strong&gt;&lt;code&gt;&lt;strong&gt;%w&lt;/strong&gt;&lt;/code&gt; - он сохраняет оригинальную ошибку, и ее потом можно распаковать через &lt;code&gt;errors.Unwrap()&lt;/code&gt;.&lt;hr&gt;&lt;h3&gt;8. ClickHouse - “Это же просто SQL”&lt;/h3&gt;&lt;p&gt;Вот тут я реально не знал специфики. В одной финтех компании активно используют ClickHouse. Дают задачку написать запрос для аналитики:&lt;pre&gt;&lt;code class=sql&gt;SELECT user_id, COUNT(*) &#xA;FROM events &#xA;WHERE event_type = &amp;#39;click&amp;#39; &#xA;  AND timestamp &amp;gt;= &amp;#39;2026-01-01&amp;#39; &#xA;GROUP BY user_id&#xA;ORDER BY COUNT(*) DESC&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Я думаю - ну нормально же? А интервьюер морщится: “А по чему у вас ORDER BY в таблице?”&lt;p&gt;Оказывается, в ClickHouse все таблицы отсортированы по ORDER BY ключу. А если фильтруешь не по нему - будет медленно.&lt;p&gt;Правильно:&lt;pre&gt;&lt;code class=sql&gt;SELECT user_id, count() as clicks&#xA;FROM events &#xA;WHERE timestamp &amp;gt;= &amp;#39;2026-01-01&amp;#39; &#xA;  AND timestamp &amp;lt; &amp;#39;2026-02-01&amp;#39;  -- Используем партиции&#xA;  AND event_type = &amp;#39;click&amp;#39;       -- После timestamp!&#xA;GROUP BY user_id&#xA;ORDER BY clicks DESC&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Урок:&lt;/strong&gt; Column-oriented БД - не PostgreSQL. У них свои заморочки.&lt;hr&gt;&lt;h3&gt;9. Kafka - “Читаю и обрабатываю”&lt;/h3&gt;&lt;p&gt;На собесе в одной high-load компании дали задачку с Kafka. Я написал что-то вроде:&lt;pre&gt;&lt;code class=go&gt;for {&#xA;    msg, err := consumer.ReadMessage(-1)&#xA;    if err == nil {&#xA;        processMessage(msg.Value) // А если упадет?&#xA;    }&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;“А что будет, если &lt;code&gt;processMessage&lt;/code&gt; упадет с паникой?” - спрашивает интервьюер.&lt;p&gt;“Ну… restart?” - говорю я.&lt;p&gt;“А offset закоммитится?”&lt;p&gt;И тут я понял, что не подумал про exactly-once delivery. Если message обработался, но offset не закоммитился - после рестарта сообщение придет снова.&lt;p&gt;Правильно - коммитить offset только после успешной обработки:&lt;pre&gt;&lt;code class=go&gt;for {&#xA;    msg, err := consumer.ReadMessage(100 * time.Millisecond)&#xA;    if err != nil {&#xA;        continue&#xA;    }&#xA;    &#xA;    if err := processMessage(msg.Value); err != nil {&#xA;        log.Printf(&amp;#34;Не смог обработать: %v&amp;#34;, err)&#xA;        continue // НЕ коммитим offset&#xA;    }&#xA;    &#xA;    consumer.CommitMessage(msg) // Коммитим только тут&#xA;}&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Урок:&lt;/strong&gt; В distributed systems каждая мелочь важна.&lt;hr&gt;&lt;h3&gt;10. System Design - тут совсем грустно&lt;/h3&gt;&lt;p&gt;Самый сложный этап. В одной крупной российской IT-компании дали спроектировать URL Shortener.&lt;p&gt;Я говорю: “Ну делаем REST API. POST /shorten принимает URL, возвращает ID. GET /:id возвращает original URL. В PostgreSQL храним mapping. Все!”&lt;p&gt;Интервьюер: “А сколько URL в день вы ожидаете?”&lt;p&gt;“Ну… много?”&lt;p&gt;“Давайте цифры”&lt;p&gt;И тут началось… Оказывается, надо считать:&lt;ul&gt;&lt;li&gt;&lt;p&gt;100 млн URL в день = ~1200 writes/sec&lt;li&gt;&lt;p&gt;Read:Write = 100:1 = 120,000 reads/sec&lt;li&gt;&lt;p&gt;Хранение за 5 лет = терабайты&lt;/ul&gt;&lt;p&gt;Один PostgreSQL не потянет. Нужен кеш (Redis), CDN для популярных ссылок, шардинг БД…&lt;p&gt;&lt;strong&gt;Урок:&lt;/strong&gt; System Design - это не “какую БД выбрать”, а понимание масштаба и trade-offs.&lt;hr&gt;&lt;h3&gt;Неожиданный бонус: финансовый бэкграунд&lt;/h3&gt;&lt;p&gt;Кстати, мой путь analyst → Go developer оказался преимуществом!&lt;p&gt;В финтех компаниях часто спрашивают не только про код, но и про бизнес-логику:&lt;ul&gt;&lt;li&gt;&lt;p&gt;“Как обеспечить exactly-once delivery для платежей?”&lt;li&gt;&lt;p&gt;“Что такое идемпотентность в контексте денежных переводов?”&lt;li&gt;&lt;p&gt;“Как бы вы реализовали matching engine?”&lt;/ul&gt;&lt;p&gt;Когда я рассказываю про опыт с ETF анализом, portfolio optimization, понимание рынков - это сразу выделяет среди других кандидатов. Интервьюеры понимают, что я не просто пишу код, а понимаю зачем.&lt;p&gt;&lt;strong&gt;Урок:&lt;/strong&gt; Ваш нетехнический опыт может стать конкурентным преимуществом!&lt;h3&gt;Что я понял после всех этих собесов&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Дело не в синтаксисе Go&lt;/strong&gt; - дело в том, как ты мыслишь как engineer&lt;li&gt;&lt;p&gt;&lt;strong&gt;Production опыт&lt;/strong&gt; стоит больше, чем знание всех фишек языка&lt;li&gt;&lt;p&gt;&lt;strong&gt;Domain knowledge&lt;/strong&gt; может быть решающим фактором&lt;li&gt;&lt;p&gt;&lt;strong&gt;Честность лучше&lt;/strong&gt; чем попытка заболтать непонимание&lt;li&gt;&lt;p&gt;&lt;strong&gt;Вопросы обратно&lt;/strong&gt; показывают, что ты думаешь о проблеме&lt;/ol&gt;&lt;h4&gt;Что реально спрашивают:&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;60% - concurrency и как оно работает под капотом&lt;li&gt;&lt;p&gt;30% - databases и distributed systems&lt;li&gt;&lt;p&gt;10% - алгоритмы (и то не leetcode, а практические)&lt;/ul&gt;&lt;h4&gt;Что помогло получить офферы:&lt;/h4&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Рассказывать про реальные проблемы&lt;/strong&gt; которые решал&lt;li&gt;&lt;p&gt;&lt;strong&gt;Объяснять trade-offs&lt;/strong&gt; - почему выбрал именно это решение&lt;li&gt;&lt;p&gt;&lt;strong&gt;Признавать пробелы&lt;/strong&gt; - “не знаю, но думаю так…”&lt;li&gt;&lt;p&gt;&lt;strong&gt;Задавать вопросы&lt;/strong&gt; - уточнять требования, обсуждать альтернативы&lt;/ol&gt;&lt;hr&gt;&lt;h3&gt;Что дальше: от статьи к практике&lt;/h3&gt;&lt;p&gt;Все эти материалы - результат месяцев подготовки и анализа реальных собеседований. Но это только верхушка айсберга.&lt;p&gt;Запускаю &lt;strong&gt;Telegram канал &lt;a class=mention href=https://habr.com/users/go_interview_prep_ru&gt;@go_interview_prep_ru&lt;/a&gt;&lt;/strong&gt;, где буду регулярно делиться:&lt;p&gt;📝 &lt;strong&gt;Подробными разборами&lt;/strong&gt; задач с реальных собесов&lt;br&gt;🧠 &lt;strong&gt;Еженедельными Go quiz&lt;/strong&gt; с объяснениями&lt;br&gt;💡 &lt;strong&gt;Инсайдами про процессы&lt;/strong&gt; топовых компаний&lt;br&gt;🔧 &lt;strong&gt;Практическими советами&lt;/strong&gt; для Senior уровня&lt;br&gt;💰 &lt;strong&gt;Данными по зарплатам&lt;/strong&gt; и market trends&lt;p&gt;Фокус канала - не теория из учебников, а &lt;strong&gt;реальные вопросы&lt;/strong&gt;, которые задают на собесах прямо сейчас.&lt;p&gt;Также готовлю &lt;strong&gt;полноценный курс&lt;/strong&gt; по подготовке к Go интервью. В основе - реальный опыт, настоящие задачи, проверенные на практике подходы.&lt;h4&gt;🎯 Для кого это будет полезно:&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Middle → Senior&lt;/strong&gt; Go разработчики&lt;li&gt;&lt;p&gt;&lt;strong&gt;Backend engineers&lt;/strong&gt; из других языков, переходящие на Go&lt;li&gt;&lt;p&gt;&lt;strong&gt;Финтех разработчики&lt;/strong&gt; (особенно ценю domain expertise!)&lt;li&gt;&lt;p&gt;&lt;strong&gt;Career changers&lt;/strong&gt; как я - никогда не поздно расти&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Подписывайтесь:&lt;/strong&gt; &lt;a href=https://t.me/go_interview_prep_ru rel=&#34;noopener noreferrer nofollow&#34;&gt;t.me/go_interview_prep_ru&lt;/a&gt;&lt;hr&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>Zmey56</author>
      <guid>https://habr.com/ru/articles/1030108/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030108</guid>
      <pubDate>Thu, 30 Apr 2026 11:48:47 +0000</pubDate>
    </item>
    <item>
      <title>Unity Builder — как мы победили боль с ручными сборками и написали своё приложение</title>
      <link>https://habr.com/ru/articles/1029938/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029938</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Всем привет! Продолжаю свой мониторинг по упрощению работы с Unity. В прошлой статье я писал о &lt;a href=https://github.com/c3n9/CraftHub rel=&#34;noopener noreferrer nofollow&#34;&gt;JSON Viewer (CraftHub)&lt;/a&gt; — инструменте, который упрощает работу с конфигами в формате JSON.&lt;hr&gt;&lt;h3&gt;Откуда растут ноги&lt;/h3&gt;&lt;p&gt;Я всё чаще сталкиваюсь с одной и той же рутиной — и на работе, и в личных проектах. Каждый раз, когда нужен свежий билд какого-либо приложения, приходится вручную собирать, после ещё закидывать файлы куда нужно… и так по кругу для каждой платформы. Это личная боль. Поэтому последние несколько месяцев я занимаюсь автоматизацией и написанием пайплайнов для своих и рабочих проектов.&lt;p&gt;Примерно полгода в нашем рабочем кругу не утихал один и тот же вопрос: &lt;strong&gt;как автоматизировать сборку Unity-проекта?&lt;/strong&gt; Хотелось что-то максимально незамысловатое, наглядное и простое — без лишних церемоний. По простоте и доступности это не avalonia, которую можно спокойно собрать на сервере.&lt;hr&gt;&lt;h3&gt;Первая попытка: Jenkins на своём сервере&lt;/h3&gt;&lt;p&gt;Мои друзья (кстати, вот &lt;a class=mention href=https://habr.com/users/crackanddie&gt;@crackanddie&lt;/a&gt; одного из них) первыми бросились в бой и попробовали Jenkins на собственном сервере. Настроили пайплайны, всё красиво… но идея быстро споткнулась о суровую реальность:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Unity поначалу вставал с большим трудом&lt;li&gt;&lt;p&gt;Лицензия оказалась настоящим квестом — получить Community-ключ так и не получилось&lt;/ul&gt;&lt;p&gt;Пошли дальше.&lt;hr&gt;&lt;h3&gt;Вторая попытка: GitHub Actions + game-ci&lt;/h3&gt;&lt;p&gt;Спустя полгода я решил разобраться в вопросе сам и подключил к поискам нейросеть. Она подсказала &lt;a href=https://github.com/game-ci/unity-builder rel=&#34;noopener noreferrer nofollow&#34;&gt;unity-builder от game-ci&lt;/a&gt; — на первый взгляд идеальный вариант.&lt;p&gt;Быстро добавили все данные от аккаунта в &lt;strong&gt;Repository Secrets&lt;/strong&gt;, настроили workflow — и поехали. Вот что получилось:&lt;pre&gt;&lt;code class=yaml&gt;name: Unity Build &amp;amp; Release&#xA;&#xA;on:&#xA;  workflow_dispatch:&#xA;&#xA;permissions:&#xA;  contents: write&#xA;&#xA;jobs:&#xA;  build-windows:&#xA;    name: Build Windows (x64)&#xA;    runs-on: ubuntu-latest&#xA;    steps:&#xA;      - name: Free disk space&#xA;        uses: jlumbroso/free-disk-space@main&#xA;        with:&#xA;          tool-cache: false&#xA;          android: true&#xA;          dotnet: true&#xA;          haskell: true&#xA;          large-packages: true&#xA;          swap-storage: true&#xA;&#xA;      - name: Checkout&#xA;        uses: actions/checkout@v4&#xA;        with:&#xA;          lfs: true&#xA;&#xA;      - name: Cache Library&#xA;        uses: actions/cache@v4&#xA;        with:&#xA;          path: Library&#xA;          key: Library-Windows-${{ hashFiles(&amp;#39;Assets/**&amp;#39;, &amp;#39;Packages/**&amp;#39;, &amp;#39;ProjectSettings/**&amp;#39;) }}&#xA;          restore-keys: Library-Windows-&#xA;&#xA;      - name: Build&#xA;        uses: game-ci/unity-builder@v4&#xA;        env:&#xA;          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}&#xA;          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}&#xA;          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}&#xA;        with:&#xA;          unityVersion: 2021.3.45f2&#xA;          targetPlatform: StandaloneWindows64&#xA;          buildName: ${{ github.event.repository.name }}&#xA;          buildsPath: build&#xA;&#xA;      - name: Zip build&#xA;        run: |&#xA;          cd build/StandaloneWindows64&#xA;          zip -r ../../${{ github.event.repository.name }}-windows-x64.zip .&#xA;&#xA;      - name: Upload to Release&#xA;        uses: softprops/action-gh-release@v2&#xA;        with:&#xA;          tag_name: ${{ github.ref_name }}&#xA;          files: ${{ github.event.repository.name }}-windows-x64.zip&#xA;&#xA;  build-linux:&#xA;    name: Build Linux (x64)&#xA;    runs-on: ubuntu-latest&#xA;    steps:&#xA;      - name: Free disk space&#xA;        uses: jlumbroso/free-disk-space@main&#xA;        with:&#xA;          tool-cache: false&#xA;          android: true&#xA;          dotnet: true&#xA;          haskell: true&#xA;          large-packages: true&#xA;          swap-storage: true&#xA;&#xA;      - name: Checkout&#xA;        uses: actions/checkout@v4&#xA;        with:&#xA;          lfs: true&#xA;&#xA;      - name: Cache Library&#xA;        uses: actions/cache@v4&#xA;        with:&#xA;          path: Library&#xA;          key: Library-Linux-${{ hashFiles(&amp;#39;Assets/**&amp;#39;, &amp;#39;Packages/**&amp;#39;, &amp;#39;ProjectSettings/**&amp;#39;) }}&#xA;          restore-keys: Library-Linux-&#xA;&#xA;      - name: Build&#xA;        uses: game-ci/unity-builder@v4&#xA;        env:&#xA;          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}&#xA;          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}&#xA;          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}&#xA;        with:&#xA;          unityVersion: 2021.3.45f2&#xA;          targetPlatform: StandaloneLinux64&#xA;          buildName: ${{ github.event.repository.name }}&#xA;          buildsPath: build&#xA;&#xA;      - name: Zip build&#xA;        run: |&#xA;          cd build/StandaloneLinux64&#xA;          zip -r ../../${{ github.event.repository.name }}-linux-x64.zip .&#xA;&#xA;      - name: Upload to Release&#xA;        uses: softprops/action-gh-release@v2&#xA;        with:&#xA;          tag_name: ${{ github.ref_name }}&#xA;          files: ${{ github.event.repository.name }}-linux-x64.zip&#xA;&#xA;  build-macos:&#xA;    name: Build macOS (arm64)&#xA;    runs-on: ubuntu-latest&#xA;    steps:&#xA;      - name: Free disk space&#xA;        uses: jlumbroso/free-disk-space@main&#xA;        with:&#xA;          tool-cache: false&#xA;          android: true&#xA;          dotnet: true&#xA;          haskell: true&#xA;          large-packages: true&#xA;          swap-storage: true&#xA;&#xA;      - name: Checkout&#xA;        uses: actions/checkout@v4&#xA;        with:&#xA;          lfs: true&#xA;&#xA;      - name: Cache Library&#xA;        uses: actions/cache@v4&#xA;        with:&#xA;          path: Library&#xA;          key: Library-macOS-${{ hashFiles(&amp;#39;Assets/**&amp;#39;, &amp;#39;Packages/**&amp;#39;, &amp;#39;ProjectSettings/**&amp;#39;) }}&#xA;          restore-keys: Library-macOS-&#xA;&#xA;      - name: Build&#xA;        uses: game-ci/unity-builder@v4&#xA;        env:&#xA;          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}&#xA;          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}&#xA;          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}&#xA;        with:&#xA;          unityVersion: 2021.3.45f2&#xA;          targetPlatform: StandaloneOSX&#xA;          buildName: ${{ github.event.repository.name }}&#xA;          buildsPath: build&#xA;&#xA;      - name: Zip build&#xA;        run: |&#xA;          cd build/StandaloneOSX&#xA;          zip -r ../../${{ github.event.repository.name }}-macos-arm64.zip .&#xA;&#xA;      - name: Upload to Release&#xA;        uses: softprops/action-gh-release@v2&#xA;        with:&#xA;          tag_name: ${{ github.ref_name }}&#xA;          files: ${{ github.event.repository.name }}-macos-arm64.zip&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Казалось бы, вот оно счастье. Но дальше ждало разочарование.&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Сборка нашего рабочего проекта под три платформы — Windows, Linux и macOS — заняла 40 минут.&lt;/strong&gt;&lt;/blockquote&gt;&lt;p&gt;Сорок. Минут. СОРОК. Загрузка Unity, получение лицензии через unity-builder — всё это вылилось в настоящую бесконечность. Особенно обидно, когда на локальной машине те же действия занимают от силы минуты три на каждую платформу, забавный момент.&lt;p&gt;Вы скажете: &lt;em&gt;«А что насчёт self-hosted runner?»&lt;/em&gt; Пробовали. И тут тоже не срослось — активация Unity через сеть сам по себе небыстрый процесс. А пока мы настраивали self-hosted и исправляли баги, время ожидания каждой следующей ошибки росло в геометрической прогрессии. Сдали ли нервы? Может быть.&lt;hr&gt;&lt;h3&gt;Третья попытка: написать своё&lt;/h3&gt;&lt;p&gt;Изрядно намучавшись, мы с другом решили работать в долгую и &lt;strong&gt;написать собственное приложение&lt;/strong&gt;. К тому же мне давно хотелось попробовать себя в чём-то связанном с пайплайнами и нодами (написать приложение с таким функционалом) — опыт интересный.&lt;p&gt;Встречайте — &lt;strong&gt;Unity Builder&lt;/strong&gt;!&lt;blockquote&gt;&lt;p&gt;Сразу оговорюсь: мы делали проект под себя и свои потребности, поэтому некоторые решения могут показаться лишними. Но мы предусмотрели выбор — сборка прекрасно работает и без дополнительных фич.&lt;/blockquote&gt;&lt;hr&gt;&lt;h3&gt;Что умеет Unity Builder&lt;/h3&gt;&lt;h4&gt;Базовая настройка&lt;/h4&gt;&lt;p&gt;На старте вас встречает простой экран с минимально необходимым:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Путь до исполняемого файла Unity&lt;li&gt;&lt;p&gt;Папка проекта&lt;li&gt;&lt;p&gt;Версия Unity&lt;li&gt;&lt;p&gt;Выходная директория&lt;li&gt;&lt;p&gt;Название проекта&lt;/ul&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/716/1f6/3be/7161f63be1db807a6be771ccb460ad47.png alt=&#34;Страница конфигурации сборки&#34; width=1215 height=743 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/716/1f6/3be/7161f63be1db807a6be771ccb460ad47.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/716/1f6/3be/7161f63be1db807a6be771ccb460ad47.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Страница конфигурации сборки&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;База, ничего лишнего.&lt;h4&gt;Выгрузка на FTP и индексация изменений&lt;/h4&gt;&lt;p&gt;Для тех, у кого есть собственный апдейтер — есть интеграция с &lt;a href=https://github.com/CrackAndDie/HashComputer rel=&#34;noopener noreferrer nofollow&#34;&gt;Hash Computer&lt;/a&gt;. Можно проиндексировать изменения и обновлять только изменившуюся часть файлов, не заливая всё целиком.&lt;p&gt;Также есть возможность выгрузить собранный проект прямо на свой FTP-сервер — без лишних телодвижений.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/211/f88/c72/211f88c72c52d66935f720f09557b5c7.png alt=&#34;Страница индексирования и ftp&#34; width=1215 height=743 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/211/f88/c72/211f88c72c52d66935f720f09557b5c7.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/211/f88/c72/211f88c72c52d66935f720f09557b5c7.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Страница индексирования и ftp&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h4&gt;Поддержка платформ&lt;/h4&gt;&lt;p&gt;Официально в Community-версии Unity нет поддержки под Linux ARM64, поэтому на текущий момент доступны:&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Платформа&lt;th&gt;&lt;p align=left&gt;Архитектуры&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Windows&lt;td&gt;&lt;p align=left&gt;x64, x86&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Linux&lt;td&gt;&lt;p align=left&gt;x64&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;macOS&lt;td&gt;&lt;p align=left&gt;Universal (Silicon + Intel)&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;Кстати, macOS — очень удобно: собирается сразу универсальный билд, совместимый и с Apple Silicon, и с Intel. Поэтому если вы выбрали выгрузку на FTP, можно указать сразу два пути — для нашего, а может и вашего удобства.&lt;p&gt;Android пока не завезли, но будем рады помощи :)&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/b69/4cb/6e6/b694cb6e63439611fc9aa527b7d14c7f.png alt=&#34;Страница выбора платформ&#34; width=1215 height=743 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/b69/4cb/6e6/b694cb6e63439611fc9aa527b7d14c7f.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/b69/4cb/6e6/b694cb6e63439611fc9aa527b7d14c7f.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Страница выбора платформ&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;h4&gt;Пайплайн в виде нодов&lt;/h4&gt;&lt;p&gt;Вот, пожалуй, самая интересная часть. После запуска вы видите &lt;strong&gt;визуальный пайплайн в виде нодов&lt;/strong&gt; — в полоске отображаются все зависимые и дочерние процессы.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr//post_images/5dc/68d/34c/5dc68d34c53bb9981cd88eada2b43ddd.png alt=&#34;Страница пайплайна&#34; width=1215 height=743 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr//post_images/5dc/68d/34c/5dc68d34c53bb9981cd88eada2b43ddd.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr//post_images/5dc/68d/34c/5dc68d34c53bb9981cd88eada2b43ddd.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Страница пайплайна&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Несколько важных деталей о работе пайплайна:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Одновременно&lt;/strong&gt; может выполняться только один Build — он взаимодействует с Unity напрямую&lt;li&gt;&lt;p&gt;Hash Computer и выгрузка на FTP могут работать &lt;strong&gt;до 10 параллельных потоков&lt;/strong&gt; — этого более чем достаточно&lt;li&gt;&lt;p&gt;Под капотом это &lt;code&gt;SemaphoreSlim&lt;/code&gt; — простое и надёжное решение&lt;/ul&gt;&lt;p&gt;Каждый нод &lt;strong&gt;кликабельный&lt;/strong&gt;: можно тыкнуть по нему и увидеть консольный вывод в реальном времени. В случае отмены главного процесса (сборки) все дочерние процессы — выгрузка и Hash Computer — убиваются автоматически.&lt;p&gt;Карточки нодов показывают:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Текущий статус&lt;li&gt;&lt;p&gt;Прогресс выполнения&lt;li&gt;&lt;p&gt;Время выполнения каждого нода&lt;/ul&gt;&lt;p&gt;Это оказалось невероятно удобным на практике.&lt;hr&gt;&lt;h3&gt;Что нужно для запуска&lt;/h3&gt;&lt;p&gt;Всё просто: предустановите в Unity модули для нужных платформ — &lt;strong&gt;Windows, macOS, Linux&lt;/strong&gt; — и сборки будут работать для всех из них без дополнительных телодвижений.&lt;p&gt;Никаких скриптов активации, никаких танцев с лицензией, никакого Docker. Скачал, указал путь до Unity и папку проекта — и через пару кликов у тебя уже крутится пайплайн. Если Unity уже стоит с нужными модулями, первый билд можно запустить буквально за пару минут после скачивания приложения. Мы специально старались сделать порог входа минимальным — чтобы не было ощущения, что ты настраиваешь CI/CD на работе, а не собираешь свой проект дома в воскресенье вечером.&lt;hr&gt;&lt;h3&gt;Обновления&lt;/h3&gt;&lt;p&gt;Приложение обновляет себя само — все релизы подтягиваются прямо с GitHub. Внутри есть кнопка обновления: никаких ручных походов на сайт, никакого «а вдруг вышла новая версия, а я и не знаю». Просто открыл — и всё актуально.&lt;hr&gt;&lt;h3&gt;Вместо заключения&lt;/h3&gt;&lt;p&gt;Мы прошли через Jenkins с его лицензионным квестом, через GitHub Actions с сорокаминутными сборками — и в итоге написали то, что хотели с самого начала.&lt;p&gt;Проект живой: мы сами им пользуемся каждый день, поэтому баги долго не залёживаются, а хотелки постепенно превращаются в фичи. Android пока не завезли — но руки тянутся. Если хочется ускорить этот процесс или добавить что-то своё — мы очень рады PR, идеям и баг-репортам. А если просто хочется поддержать — звёздочка на GitHub тоже считается.&lt;p&gt;Ссылка на проект: &lt;a href=https://github.com/Soft-V/UnityBuilder rel=&#34;noopener noreferrer nofollow&#34;&gt;https://github.com/Soft-V/UnityBuilder&lt;/a&gt;&lt;br&gt;Ссылка на релизы: &lt;a href=https://github.com/Soft-V/UnityBuilder/releases rel=&#34;noopener noreferrer nofollow&#34;&gt;https://github.com/Soft-V/UnityBuilder/releases&lt;/a&gt;&lt;p&gt;Спасибо, что дочитали до конца. Надеемся, что Unity Builder сделает вашу разработку чуть приятнее — или хотя бы избавит от тех самых сорока минут ожидания.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>c3n9</author>
      <guid>https://habr.com/ru/articles/1029938/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029938</guid>
      <pubDate>Thu, 30 Apr 2026 11:26:13 +0000</pubDate>
    </item>
    <item>
      <title>Как дать AI-агенту не лом, а ключ от браузера: разбираем agent-browser от Vercel</title>
      <link>https://habr.com/ru/articles/1029704/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029704</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ca3/eb0/ff1/ca3eb0ff14b6c9a74387405b6c701771.png alt=&#34;agent-browser против монстра-DOM-дерева&#34; title=&#34;agent-browser против монстра-DOM-дерева&#34; width=1672 height=941 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ca3/eb0/ff1/ca3eb0ff14b6c9a74387405b6c701771.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ca3/eb0/ff1/ca3eb0ff14b6c9a74387405b6c701771.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;agent-browser против монстра-DOM-дерева&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Если вы в последнее время пытались прикрутить к своему любимому LLM-агенту возможность самостоятельно гулять по интернету, дебажить веб-приложения, и даже верстать, вы наверняка столкнулись с суровой реальностью. Оказывается, засунуть современный веб в контекстное окно нейросети — очень &amp;#34;дорогая&amp;#34; задача.&lt;p&gt;Обычно в таких случаях не глядя берут проверенные инструменты вроде Puppeteer или Playwright, которые обернуты в те самые три буквы &lt;span class=habrahidden&gt;MCP&lt;/span&gt;. Но ребята из Vercel недавно выкатили свою альтернативу — &lt;strong&gt;agent-browser&lt;/strong&gt; (cli-утилиту, написанную на связке Rust и, некогда Node, но об этом позже). Зачем понадобился еще один велосипед для автоматизации, если у нас уже есть стандарты индустрии? Давайте разбираться.&lt;h4&gt;1. Что не так с существующими решениями (Puppeteer, Playwright MCP и тд)&lt;/h4&gt;&lt;p&gt;Никто не спорит, Playwright и Puppeteer — это шедевры инженерии. Они идеально подходят для того, для чего создавались: детерминированного end-to-end тестирования, CI/CD пайплайнов и предсказуемого парсинга.&lt;p&gt;Но когда мы пытаемся передать управление браузером AI-агенту через популярный сейчас Model Context Protocol (MCP), начинается боль, и заканчиваются токены. Агенту нужно &amp;#34;видеть&amp;#34; контент страницы, чтобы понимать, куда кликать. Есть два основных способа дать ему эту возможность:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Скормить сырой HTML.&lt;/strong&gt; И моментально выжечь весь контекст на одном только DOM-дереве тяжелого SPA-приложения.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Отдать Accessibility Tree.&lt;/strong&gt; Это стандартный подход для MCP-серверов, но полные деревья весят все равно неадекватно много.&lt;/ul&gt;&lt;p&gt;Проблема совершенно не выдумана. Загляните в issue-трекеры популярных инструментов: например, в официальном репозитории &lt;code&gt;ChromeDevTools/chrome-devtools-mcp&lt;/code&gt; разработчики &lt;a href=https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/726 rel=&#34;noopener noreferrer nofollow&#34;&gt;прямо показывают в логах&lt;/a&gt;, как один только клик и снятие снимка сложной страницы (вроде Jupyter Notebook) выбивает в трубу от 15 000 до 200 000 токенов за шаг. Агент делает пару кликов, забывает, зачем вообще пришел на сайт и как его зовут, и с треском падает с ошибкой &lt;code&gt;context length exceeded&lt;/code&gt;.&lt;p&gt;К тому же, LLM часто галлюцинируют в сложных CSS-селекторах. В итоге традиционные инструменты заставляют агента жрать лишние токены и постоянно промахиваться мимо кнопок.&lt;h4&gt;2. Как Vercel избавились от лишнего и в своем же решении в том числе&lt;/h4&gt;&lt;p&gt;Команда Vercel последнее время плотно занялась AI-инструментами (тот же v0, инфра для агентов и тд) и столкнулась с очевидным затыком: им нужен был способ валидации фронтенда. Когда автономный кодинговый агент пишет компонент, он должен сам открыть браузер, покликать и убедиться, что всё работает.&lt;p&gt;Изначально они слепили гибрид: Rust-клиент плюс тяжелый фоновый процесс на Node.js. В сети до сих пор можно наткнуться на статьи, где люди жалуются на скорость agent-browser в той версии, сравнивая его с другими решениями. Но, к сожалению, или к моему счастью, ко мне в руки он попал уже тогда, когда из него полностью выпилили Node-демон.&lt;p&gt;Архитектура стала максимально простой:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Единый бинарник (100% Rust):&lt;/strong&gt; моментально парсит команды из терминала.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Прямое общение с CDP:&lt;/strong&gt; Rust дергает Chrome DevTools Protocol напрямую, без прослоек.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Zero-dependency:&lt;/strong&gt; вам больше не нужно тащить в Docker-контейнер всю экосистему Node.js.&lt;/ul&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/5de/a78/5c3/5dea785c3e0fa4444b757fc90c4bb833.png alt=&#34;Архитектура agent-browser: до и после&#34; title=&#34;Архитектура agent-browser: до и после&#34; width=1956 height=1016 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/5de/a78/5c3/5dea785c3e0fa4444b757fc90c4bb833.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/5de/a78/5c3/5dea785c3e0fa4444b757fc90c4bb833.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Архитектура agent-browser: до и после&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Главная киллер-фича — компактизация стейта. Вместо того, чтобы вываливать на агента весь DOM, agent-browser делает снимок интерактивных элементов (&lt;code&gt;snapshot -i&lt;/code&gt;) и присваивает им короткие референсы.&lt;p&gt;Для LLM вывод выглядит так:&lt;pre&gt;&lt;code class=bash&gt;button &amp;#34;Sign In&amp;#34; [ref=e1]&#xA;textbox &amp;#34;Email&amp;#34; [ref=e2]&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Это занимает пару сотен токенов. Агент понимает, что ему нужно нажать, и просто отправляет bash-команду: &lt;code&gt;agent-browser fill @e2 &amp;#34;admin&amp;#34;&lt;/code&gt;.&lt;h4&gt;3. Сравнение подходов и внезапный ответ от Microsoft&lt;/h4&gt;&lt;p&gt;Разница в подходах лучше всего видна на практике. Допустим, мы просим агента: &lt;em&gt;&amp;#34;Зайди на &lt;/em&gt;&lt;a href=http://habr.com rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;em&gt;habr.com&lt;/em&gt;&lt;/a&gt;&lt;em&gt; и кликни на первую статью&amp;#34;&lt;/em&gt;.&lt;p&gt;&lt;strong&gt;Подход классического MCP-сервера:&lt;/strong&gt;&lt;p&gt;Агент вызывает инструмент навигации. В ответ ему прилетает простыня Accessibility Tree на 20 000 токенов. LLM продирается через эти мегабайты текста, чтобы найти заголовок, а затем пытается сгенерировать точный селектор для клика: &lt;code&gt;browser_click({ &amp;#34;selector&amp;#34;: &amp;#34;tm-articles-list__after-article h3 &amp;gt; a.title-link&amp;#34; })&lt;/code&gt;. Шаг влево, шаг вправо в верстке — и клик улетает в пустоту, &lt;s&gt;или в диалог о согласии на куки&lt;/s&gt;.&lt;p&gt;&lt;strong&gt;Подход agent-browser:&lt;/strong&gt;&lt;p&gt;Агент плюет в bash одну строчку: &lt;code&gt;agent-browser open &lt;/code&gt;&lt;a href=https://habr.com rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;code&gt;https://habr.com&lt;/code&gt;&lt;/a&gt;. Затем делает &lt;code&gt;agent-browser snapshot&lt;/code&gt;, получает плоский короткий список, где нужная ссылка помечена как &lt;code&gt;[ref=e42]&lt;/code&gt;, и отправляет &lt;code&gt;agent-browser click @e42&lt;/code&gt;. Риск промахнуться стремится к нулю.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/472/441/62e/47244162ed9818075131a2e7772b9b4a.png alt=&#34;habr.com размеченный референсами agent-browser&#34; title=&#34;habr.com размеченный референсами agent-browser&#34; width=1672 height=941 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/472/441/62e/47244162ed9818075131a2e7772b9b4a.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/472/441/62e/47244162ed9818075131a2e7772b9b4a.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;habr.com размеченный референсами agent-browser&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;А как же playwright-cli?&lt;/strong&gt;&lt;p&gt;Самое смешное, что в Microsoft тоже осознали тупиковость классического MCP для агентов. Буквально недавно они выкатили свой ответ — &lt;a href=https://github.com/microsoft/playwright-cli rel=&#34;noopener noreferrer nofollow&#34;&gt;&lt;code&gt;@playwright/cli&lt;/code&gt;&lt;/a&gt; , специальный интерфейс именно для AI-агентов. Не путать с обычным &lt;a href=https://github.com/microsoft/playwright rel=&#34;noopener noreferrer nofollow&#34;&gt;playwright раннером&lt;/a&gt;.&lt;p&gt;Они пошли по тому же пути и перевели агентов на bash. Теперь агент через Playwright тоже может написать &lt;code&gt;playwright-cli click e15&lt;/code&gt;. Инструмент сам разбирается с селекторами под капотом, а вместо того, чтобы стримить гигантские деревья в контекст LLM, сохраняет стейт на диск. Это кардинально снижает расход токенов.&lt;p&gt;Сравнивая agent-browser и playwright-cli, мы видим битву двух одинаковых философий. Отличие в деталях: playwright-cli тянет за собой всю мощь (и тяжесть) экосистемы Node/Playwright, предлагая привычный инструментарий для тех, кто уже плотно сидит на стеке с Playwright. agent-browser же подкупает своей &lt;s&gt;хайповой&lt;/s&gt; нативной Rust-природой и абсолютным минимализмом — один маленький бинарник, который идеально ложится в легковесные контейнеры.&lt;h4&gt;4. Заключение&lt;/h4&gt;&lt;p&gt;Индустрия браузерной автоматизации прямо сейчас дробится на ниши. Сегодня есть разные способы дать возможность агенту пользоваться браузером, и каждый из них по-своему хорош.&lt;p&gt;Если вам нужен сложный скрапинг данных с обходом антифрод-систем или жесткие E2E-тесты в пайплайнах, вы всё равно будете писать код на классическом Playwright. А если вы хотите, чтобы кодинговый агент сам проверял, что кнопка в вашем React-Tailwind-VibeСode-WebApp работала так, как нужно, используйте легковесные обертки вроде &lt;code&gt;agent-browser&lt;/code&gt; или нового &lt;code&gt;playwright-cli&lt;/code&gt;.&lt;p&gt;Все эти подходы отлично работают, если применять их по назначению. Но самая важная мысль, которую я хочу, чтобы вы унесли с собой из этой статьи — &lt;strong&gt;не используйте MCP для браузера&lt;/strong&gt;. Поберегите свои контекстные окна и деньги на API.&lt;p&gt;Ну, а на дорожку, пощупать agent-browser можно буквально в пару строк:&lt;pre&gt;&lt;code class=bash&gt;npm install -g agent-browser&#xA;npx skills add vercel-labs/agent-browser&#xA;agent-browser install&#xA;agent-browser open https://habr.com&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;А как ваши агенты пользуются браузерами? Есть ли решения еще более оптимизированные? И самое главное, как много токенов вы в свое время сожгли открытием одной веб-страницы?&lt;blockquote&gt;&lt;p&gt;А если вам понравилось что тут написано или вы заинтересованы агентским кодингом, то приглашаю в свой телеграм канал: &lt;a href=https://t.me/+vTV27q9gpao2ODQy rel=&#34;noopener noreferrer nofollow&#34;&gt;OpenKirill: AI Coding и другие приколы&lt;/a&gt;. Мы там разбираем тулинг, следим за новыми трендами в AI кодинге, и хорошо проводим время.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>kee_real</author>
      <guid>https://habr.com/ru/articles/1029704/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1029704</guid>
      <pubDate>Thu, 30 Apr 2026 11:14:16 +0000</pubDate>
    </item>
    <item>
      <title>Как дизайнеру подготовить сложную веб-анимацию для разработчика: три сценария из практики</title>
      <link>https://habr.com/ru/articles/1030086/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030086</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/9fe/378/6f8/9fe3786f80ec179a2825af1282c80596.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/9fe/378/6f8/9fe3786f80ec179a2825af1282c80596.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/9fe/378/6f8/9fe3786f80ec179a2825af1282c80596.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Все мы знаем этот рабочий любовный треугольник: клиент — дизайнер — разработчик.&lt;p&gt;&lt;strong&gt;Клиент:&lt;/strong&gt; «А давайте при движении мышки у нас из частиц соберётся грузовик, а потом ещё и датчик слежения за ним?»&lt;p&gt;&lt;strong&gt;Дизайнер: &lt;/strong&gt;«Я нарисовал. Примера анимации нет, но я тебе сейчас всё объясню на словах…»&lt;p&gt;&lt;strong&gt;Разработчик&lt;/strong&gt;: «А можно хотя бы референс, механику движения и понимание, что должно происходить?»&lt;p&gt;Знакомо? Особенно когда клиент хочет «как у Apple», дизайнер придумал красоту, а фронтендеру нужно понять, как это реализовать и сколько времени закладывать. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e48/247/28d/e4824728db921c222eb66a04c7622602.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e48/247/28d/e4824728db921c222eb66a04c7622602.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e48/247/28d/e4824728db921c222eb66a04c7622602.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Расскажу, как облегчить жизнь всем троим, когда дело доходит до сложных интерактивных анимаций, геймификации, скролл-эффектов и прочей «вау-фигни».&lt;h3&gt;Ситуация 1. Клиент хочет эффект воды, ряби или капель на hero-блоке при движении курсора&lt;/h3&gt;&lt;p&gt;Ситуация не то чтобы страшная, но попотеть придётся. Есть несколько вариантов решения этого вопроса. &lt;p&gt;&lt;strong&gt;Вариант 1: &lt;/strong&gt;если есть референс — вообще замечательно! Беру страницу с похожим эффектом, нажимаю на правую кнопку мыши или на тачпад, выбираю «Просмотр кода», выделяю весь код и вставляю в любой текстовый редактор. Я пользуюсь стандартным Google Docs, но можно и блокнотом, ну, или скачивайте в формате .txt. &lt;p&gt;&lt;strong&gt;В моём случае был такой сайт:&lt;/strong&gt;&lt;p&gt;&lt;span class=habrahidden&gt;https://bfd.su/&lt;/span&gt;&lt;p&gt;Далее иду в Grok, прикрепляю файл и пишу промпт:&lt;p&gt;«Из этого кода надо вычленить кусок, который отвечает за анимацию воды на hero-блоке» &lt;p&gt;Вот какой результат выдаёт нейронка: &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/752/b36/254/752b3625409648cbb4c8d77369ddea8f.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/752/b36/254/752b3625409648cbb4c8d77369ddea8f.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/752/b36/254/752b3625409648cbb4c8d77369ddea8f.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Иииии...ничего не заработало, иду дальше.&lt;p&gt;Пишу следующий промпт: «Прикрути к коду тёмный фон, чтобы хорошо посмотреть эффект воды»&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/348/6b2/89a/3486b289a914a9be0f5bc8add9cdebc0.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/348/6b2/89a/3486b289a914a9be0f5bc8add9cdebc0.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/348/6b2/89a/3486b289a914a9be0f5bc8add9cdebc0.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;На превью видно, что фон есть, но эффект еле-еле заметный.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/a51/f32/d6b/a51f32d6bfca3802b94a62644979c8f3.jpg width=1920 height=600 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/a51/f32/d6b/a51f32d6bfca3802b94a62644979c8f3.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/a51/f32/d6b/a51f32d6bfca3802b94a62644979c8f3.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Но мы не сдаёмся!&lt;p&gt;Пишу дальше: «Код не работает, найди ошибки»&lt;div class=tm-iframe_temp data-src=https://embedd.srv.habr.com/iframe/69f32e870d23650234eb4c23 data-style id=69f32e870d23650234eb4c23 width data-habr-games&gt;&lt;/div&gt;&lt;p&gt;Тут уже виден сам эффект: он реагирует на мышь, и рябь есть. Этот код можно скопировать, приложить к макету и отдать разработчикам. Если требуется замедлить эффект, сделать капли более крупными или мелкими — меняем настройки, но лучше попросить об этом знающих и умеющих людей. В конце концов, можно попросить об этом и нейронку, если таковых нет.&lt;p&gt;&lt;strong&gt;Вариант 2: &lt;/strong&gt;Можно найти похожий эффект в сервисах с готовым кодом и отправить его в качестве рабочего варианта. &lt;p&gt;Например, такие штуки можно искать на &lt;strong&gt;CodePen&lt;/strong&gt;. &lt;p&gt;Пишу в поисковой строке «Water ripple effect» и смотрю, что выдаст поиск.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/a13/d47/68a/a13d4768a5f2a01eff174d828cb31b37.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/a13/d47/68a/a13d4768a5f2a01eff174d828cb31b37.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/a13/d47/68a/a13d4768a5f2a01eff174d828cb31b37.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Нахожу наиболее привлекательный вариант и открываю код. Можно также менять настройки, а можно отдать как есть. &lt;p&gt;CodePen — это кладезь разных эффектов и анимаций, советую его использовать для насмотренности. Там можно кидать анимации в избранное, чтобы не потерять)&lt;br&gt;&lt;br&gt;&lt;strong&gt;Небольшой совет: &lt;/strong&gt;обращайте внимание на название самого эффекта. Его можно скопировать и использовать для более точного поиска. &lt;h3&gt;Ситуация 2. Клиент хочет «взрыв-схему» бургера (или любого продукта)&lt;/h3&gt;&lt;p&gt;Клиент открывает свою бургерную и хочет что-то УНИКАЛЬНОЕ, как у Apple, и чтобы всё вертелось, крутилось, но было минималистично и лаконично. В общем, хочет «взрыв-схему» бургера, завязанную на скролл.&lt;p&gt;Ок! Хочет — буду делать.&lt;h4&gt;Шаг 1. Генерирую исходную картинку&lt;/h4&gt;&lt;p&gt;На просторах Pinterest я нашла промпт, который бы мне подошёл, чтобы сократить время. &lt;p&gt;&lt;strong&gt;Промпт:&lt;/strong&gt;&lt;p&gt;«A hyper-realistic, mid-air exploded view of a towering veggie burger with multi-grain bun, sharp cheddar, grilled mushroom patty, roasted red pepper, and crisp breadcrumbs, and red cherry tomatoes hover in dynamic suspension around the ingredients, as if caught in a flavorful gust of wind. The lighting is warm and directional, highlighting moisture droplets, textures, and sheen across every layer. Photorealistic rendering with extreme depth of field, dramatic shadows, and vibrant organic color grading evokes the sense of a fresh, gourmet plant-based meal captured in an action freeze-frame»&lt;p&gt;Иду в бесплатный Gemini и вставляю промпт.&lt;p&gt;Получается такой результат:&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/8c4/c9c/ec8/8c4c9cec82b42dbd1e2032dc70319e2c.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/8c4/c9c/ec8/8c4c9cec82b42dbd1e2032dc70319e2c.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/8c4/c9c/ec8/8c4c9cec82b42dbd1e2032dc70319e2c.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h4&gt;Шаг 2. Собираю бургер обратно &lt;/h4&gt;&lt;p&gt;Всё в том же Gemini собираю бургер.&lt;p&gt;«Assemble the burger–make sure it doesn&amp;#39;t fall apart»&lt;p&gt;Получаю две картинки:&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e9c/d4a/c45/e9cd4ac456e8d7347e19dee54ffc9902.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e9c/d4a/c45/e9cd4ac456e8d7347e19dee54ffc9902.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e9c/d4a/c45/e9cd4ac456e8d7347e19dee54ffc9902.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/5b7/2f7/2d6/5b72f72d69df1027d50162addc57f708.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/5b7/2f7/2d6/5b72f72d69df1027d50162addc57f708.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/5b7/2f7/2d6/5b72f72d69df1027d50162addc57f708.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Иду в Figma и готовлю материал:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Делаю однотонный фон, желательно того же цвета, что и фон сайта.&lt;li&gt;&lt;p&gt;Делаю одинаковый формат — это важно! Один фон, одно позиционирование и один размер картинки.&lt;/ol&gt;&lt;h4&gt;Шаг 3. Делаю плавный переход в Kling&lt;/h4&gt;&lt;p&gt;Выгружаю картинки в формате JPEG и иду в Kling для генерации видео. Зачем? Чтобы потом покадрово разложить видео и привязать кадры к скроллу.&lt;p&gt;Выбираю формат работы — «Generate».&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/01d/9a8/35a/01d9a835ac43db1d6296000be7e03e63.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/01d/9a8/35a/01d9a835ac43db1d6296000be7e03e63.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/01d/9a8/35a/01d9a835ac43db1d6296000be7e03e63.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Выставляю первый и финальный кадр самого видео.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/14c/338/e2f/14c338e2f83b3fc24868183eb1ce4bbe.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/14c/338/e2f/14c338e2f83b3fc24868183eb1ce4bbe.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/14c/338/e2f/14c338e2f83b3fc24868183eb1ce4bbe.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Получаю видео с зацикленным результатом.&lt;div class=tm-iframe_temp data-src=https://embedd.srv.habr.com/iframe/69f32fd3ddfeeb036a948ba9 data-style id=69f32fd3ddfeeb036a948ba9 width data-habr-games&gt;&lt;/div&gt;&lt;h4&gt;Шаг 4. Превращаю видео в интерактивный скролл-эффект&lt;/h4&gt;&lt;p&gt;Для классного результата нужны промпты, спецификация и вот это вот всё. Я, честно, лентяйка, поэтому просто переписала уже готовые документы под нужную тематику, также через Grok.&lt;p&gt;Томить не буду, просто вставлю всё сюда.&lt;p&gt;&lt;a href=https://disk.360.yandex.ru/d/FJvONpkNxTjAMw rel=&#34;noopener noreferrer nofollow&#34;&gt;Папка с файлами.&lt;/a&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/de3/678/ae1/de3678ae1c24c795de028fc2c6391ec3.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/de3/678/ae1/de3678ae1c24c795de028fc2c6391ec3.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/de3/678/ae1/de3678ae1c24c795de028fc2c6391ec3.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Беру 3-й промпт, вставляю его и прикрепляю все остальные документы из папки.&lt;p&gt;&lt;strong&gt;Промпт:&lt;/strong&gt;&lt;p&gt;«Мне нужно создать лендинг для бургерного заведения. Я хочу добавить scroll-анимацию в центральную секцию сайта — бургер «разлетается» на слои по мере скролла (exploded view). Мне нужен премиальный, высококачественный вид, далёкий от типичного фастфуд-дизайна.&lt;p&gt;Файлы в проекте:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;a href=http://landing.md rel=&#34;noopener noreferrer nofollow&#34;&gt;landing.md&lt;/a&gt; — полное ТЗ с текстами, структурой страницы и описанием секций. Следуй ему.&lt;li&gt;&lt;p&gt;&lt;a href=http://scroll-animation-config.md rel=&#34;noopener noreferrer nofollow&#34;&gt;scroll-animation-config.md&lt;/a&gt; — конфигурация scroll-анимации: пайплайн, параметры canvas, предзагрузка кадров, логика скролла. Используй как техническую спецификацию.&lt;li&gt;&lt;p&gt;&lt;a href=http://video.mp rel=&#34;noopener noreferrer nofollow&#34;&gt;video.mp&lt;/a&gt;4 — исходное видео с анимацией бургера. Из него нужно извлечь кадры для scroll-анимации.&lt;li&gt;&lt;p&gt;logo.png — логотип для хедера.&lt;/ul&gt;&lt;p&gt;Техстек:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Next.js&lt;li&gt;&lt;p&gt;Tailwind CSS&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Важно:&lt;/strong&gt; Для извлечения кадров из видео нужен FFmpeg. Если не установлен — сначала установи его. Конвертация &lt;a href=http://video.mp rel=&#34;noopener noreferrer nofollow&#34;&gt;video.mp&lt;/a&gt;4 → секвенция кадров WebP описана в &lt;a href=http://scroll-animation-config.md rel=&#34;noopener noreferrer nofollow&#34;&gt;scroll-animation-config.md&lt;/a&gt;»&lt;p&gt;&lt;strong&gt;ВАЖНО! &lt;/strong&gt;Названия файлов должны совпадать с названиями в промпте.&lt;p&gt;Добавляю всё это в Claude и получаю такой результат:&lt;div class=tm-iframe_temp data-src=https://embedd.srv.habr.com/iframe/69f3331442c0bc03ac31f1b0 data-style id=69f3331442c0bc03ac31f1b0 width data-habr-games&gt;&lt;/div&gt;&lt;p&gt;&lt;span class=habrahidden&gt;file:///Users/kate/Downloads/grind-burger-preview%20(1)/index.html&lt;/span&gt;&lt;p&gt;Вытаскиваю рабочий кусок кода и отдаю фронтам.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/72a/b95/409/72ab95409a4054f46ad1cf877ebf9c76.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/72a/b95/409/72ab95409a4054f46ad1cf877ebf9c76.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/72a/b95/409/72ab95409a4054f46ad1cf877ebf9c76.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Врать не буду — чтобы получился крутой результат, мне пришлось прям попотеть: то нейминги не совпадают, то он не брал мой бургер, а рисовал свой схематичный, то Claude отваливался и делал чушню. Так что не совершайте моих ошибок и идите чётко по алгоритму.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/5c8/e35/d16/5c8e35d16c4a75c1cc52724a46c70351.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/5c8/e35/d16/5c8e35d16c4a75c1cc52724a46c70351.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/5c8/e35/d16/5c8e35d16c4a75c1cc52724a46c70351.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h3&gt;Ситуация 3. Логотип распадается и собирается из частиц&lt;/h3&gt;&lt;p&gt;Идея классная, но что с ней делать?! Объяснить такое тяжело, но есть несколько вариантов.&lt;p&gt;Можно вернуться к предыдущим сценариям, а можно перейти в &lt;strong&gt;Spline&lt;/strong&gt;. Там есть возможность для создания своих анимаций, а ещё внедрение уже придуманных эффектов.&lt;p&gt;Перехожу в раздел «Community» и вбиваю в поиске «Particles».&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/9bf/66d/c98/9bf66dc98dcbba09b869b47deb3d3a6c.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/9bf/66d/c98/9bf66dc98dcbba09b869b47deb3d3a6c.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/9bf/66d/c98/9bf66dc98dcbba09b869b47deb3d3a6c.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Нахожу нужный референс:&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/fba/316/45a/fba31645a3b67e231ea77ad173053e12.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/fba/316/45a/fba31645a3b67e231ea77ad173053e12.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/fba/316/45a/fba31645a3b67e231ea77ad173053e12.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/5ae/56e/507/5ae56e5078e5428cc400eaba5e3682ef.jpg width=1920 height=1080 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/5ae/56e/507/5ae56e5078e5428cc400eaba5e3682ef.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/5ae/56e/507/5ae56e5078e5428cc400eaba5e3682ef.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Захожу в рабочее пространство самого эффекта&lt;li&gt;&lt;p&gt;Загружаю в него 3D-рендер модели. В моём случае это был сложный грузовик, но в целом, это может быть что вашей душе угодно. Вопрос лишь в том, что чем проще фигура, тем лучше она будет выглядеть — это в целом логично. Условно, квадрат или несложный логотип будут более эффектными и менее замороченными по настройкам.&lt;li&gt;&lt;p&gt;Далее кручу, верчу и настраиваю сами частицы, делаю их крупнее или мельче, много или мало. Настраиваю интерактивное взаимодействие анимации и мышки, скорость реакции, резкость — ну, вы поняли. Лучше это всё отдавать вашему моушен-дизайнеру, но в целом, разобраться реально. Подробнее на видео:&lt;li&gt;&lt;p&gt;После того, как видимый результат начал вызывать экстаз и истерический восторг, экспортирую скрипт, который описывает данную анимацию. &lt;li&gt;&lt;p&gt;Отдаю все файлы фронту и живу в мире и благополучии.&lt;/ol&gt;&lt;div class=tm-iframe_temp data-src=https://embedd.srv.habr.com/iframe/69f336863c0662029425f004 data-style id=69f336863c0662029425f004 width data-habr-games&gt;&lt;/div&gt;&lt;p&gt;Есть одно НО — этот сервис платный.&lt;p&gt;&lt;strong&gt;Итоговые лайфхаки для дизайнеров и разработчиков:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Не пытайтесь объяснять сложную анимацию только на словах. Лучше сразу прикладывайте референсы, код и видео.&lt;li&gt;&lt;p&gt;CodePen, Spline Community и готовые эффекты на GitHub — это ваши лучшие друзья.&lt;li&gt;&lt;p&gt;ИИ (Grok, Claude и Kling) сильно ускоряет процесс, но работает лучше всего в связке: один генерит визуал, второй — код, а третий помогает отлаживать результаты.&lt;li&gt;&lt;p&gt;Самый быстрый путь к вау-эффектам — комбинация: референс → ИИ-разбор → доработка → готовый код.&lt;/ul&gt;&lt;p&gt;Сложные анимации больше не повод для войны в чате. Теперь это просто ещё один инструмент, которым можно пользоваться быстро и с удовольствием.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>tim_kate1010</author>
      <guid>https://habr.com/ru/articles/1030086/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030086</guid>
      <pubDate>Thu, 30 Apr 2026 11:07:29 +0000</pubDate>
    </item>
    <item>
      <title>Что будет, если пихать кофеин под глаза</title>
      <link>https://habr.com/ru/companies/geltek/articles/1030048/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030048</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Предположим, у вас под глазами фофаны. Не те, которые случаются, когда по лицу прилетело, а сугубо физиологические.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d05/576/bf9/d05576bf99478fd24378e477843ca715.jpeg alt=Источник title=Источник width=753 height=529 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d05/576/bf9/d05576bf99478fd24378e477843ca715.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d05/576/bf9/d05576bf99478fd24378e477843ca715.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;a href=https://kulturologia.ru/blogs/161116/32265/&gt;Источник&lt;/a&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Физиология такова (и никакова иначе), что кожа под глазами тонкая, капилляры просвечивают, жидкость застаивается, отсюда отёки и синяки (ну или, по-научному, фофаны). Один из рабочих способов с ними справиться — это кофеин. Влить по шоту эспрессо на каждый фофан — идея красивая, но, во-первых, горячо, во-вторых, не сработает.&lt;p&gt;Кофеин можно есть, пить и мазать. Он существует даже в инъекционном виде, медики его ставят, когда нужно простимулировать сосудодвигательный центр. Каждый способ доставки даёт свой эффект. Контактный — локально сужает капилляры, снижает проницаемость сосудистой сетки и выпинывает жидкость из межклеточного пространства.&lt;p&gt;Мы в Гельтеке 30 лет делаем медицинские гели, 10 лет — уходовую косметику, любим сложные задачи. Фофаны — одна из таких. У нас есть средство с кофеином, но это, скорее, экстренная косметическая помощь. А теперь мы сделали штуку посерьёзнее: кофеина там больше, он высвобождается постепенно и работает на перспективу.&lt;h3&gt;Из чего состоят тёмные круги&lt;/h3&gt;&lt;p&gt;Группа пластических хирургов в 2016 году перелопатила доступную на тот момент литературу по тёмным кругам и &lt;a href=https://pmc.ncbi.nlm.nih.gov/articles/PMC4924417/pdf/JCAS-9-65.pdf&gt;выдала саммари&lt;/a&gt;: что это такое, откуда берётся, как лечить.&lt;p&gt;С возрастом кости лица теряют объём, особенно в зоне орбиты — это, собственно, костная впадина, в которой находится глаз. Жир съезжает вниз, а кожа над ямкой западает, получается носослёзная борозда. Это не пигмент, а тупо тень от трёхмерного углубления. Верхний свет делает её заметнее, прямой — скрывает.&lt;p&gt;Кожа нижнего века — одна из самых тонких на теле. Под ней почти нет подкожного жира, поэтому через неё просвечивает круговая мышца глаза и густая сосудистая сеть. Это даёт синеватый или фиолетовый оттенок. Плюс под глазами часто расплёскиваются продукты распада гемоглобина: гемосидерин и биливердин. Они буквально окрашивают кожу изнутри в коричневато-зелёный цвет. Кофеин тут косвенно помогает: укрепляет сосудистую стенку, делает их чуть менее выраженными. Но убрать полностью он не может, потому что синяк и есть сосуд. Кожа век тонкая, у кого-то настолько, что без консилера человек вообще на улицу не выходит. Это генетика.&lt;p&gt;То, что заложено с рождения, гелем не переписать.&lt;p&gt;А вот то, что усиливается от недосыпа и усталости, — это уже рабочая зона.&lt;p&gt;Меланин тоже добавляет красок. Причины: солнце, гормональные изменения, аллергия, контактный дерматит. Японские учёные в своё время &lt;a href=https://pubmed.ncbi.nlm.nih.gov/16792642/&gt;взяли&lt;/a&gt; биопсии у пациентов с пигментными кругами и обнаружили, что у всех был меланоз дермы, причём интенсивность кругов менялась каждый день. Когда дерма отекает и утолщается, свет рассеивается иначе, и тот же самый меланин выглядит темнее. То есть даже чисто пигментные круги частично управляются сосудистым компонентом.&lt;h3&gt;Почему кофеин вообще делает с сосудами что-то полезное&lt;/h3&gt;&lt;p&gt;Кофеин — конкурентный антагонист аденозиновых рецепторов. Аденозин в норме расширяет сосуды, связывается с рецепторами A1 и A2, и те дают команду «расслабьтесь». Кофеин занимает эти рецепторы раньше аденозина, и сосуды, соответственно, этой команды не получают.&lt;p&gt;Параллельно кофеин блокирует фермент фосфодиэстеразу — это, условно, тормозная педаль для внутриклеточных процессов. Есть такое вещество — циклический АМФ. Это внутриклеточный посыльный, который запускает метаболические процессы: расщепление жиров, гликогена, выведение жидкости. В норме фосфодиэстераза гасит АМФ, чтобы эти процессы не разгонялись слишком сильно. Кофеин забирает фосфодиэстеразу на себя — циклический АМФ накапливается, метаболизм ускоряется, жидкость начинает активнее выводиться из тканей. Именно поэтому кофеин появляется в антицеллюлитных кремах и мезотерапевтических липолитиках.&lt;p&gt;Для кожи вокруг глаз это означает сразу три вещи: дренаж жидкости из межклеточного пространства (проще говоря, уходит отёк), сужение сосудов (отсюда уменьшение видимых синяков) и антиоксидантную защиту (кофеин нейтрализует свободные радикалы, которые разрушают коллаген).&lt;h3&gt;Японцы решили это проверить и получили неожиданные данные&lt;/h3&gt;&lt;p&gt;В 2002 году учёные из Медицинского колледжа Осаки &lt;a href=https://pubmed.ncbi.nlm.nih.gov/12062222/&gt;задались вопросом&lt;/a&gt;: окей, кофеин снижает кровоток в мозге, это мы знаем, но что именно он делает с сосудами внутри глаза, особенно в зоне зрительного нерва?&lt;p&gt;Важный контекст: плохое кровоснабжение зрительного нерва — самостоятельный фактор риска его деградации. Нерв получает меньше крови, а значит, меньше кислорода и питательных веществ, из-за этого развивается ишемия, и в конечном итоге зрение ухудшается. Это происходит даже у пациентов с нормальным внутриглазным давлением — так называемая нормотензивная глаукома.&lt;p&gt;Поэтому вопрос не академический, а сугубо прикладной.&lt;p&gt;В исследование взяли 10 человек: 5 мужчин и 5 женщин, возраст 25–44 года, никаких глазных болезней. Дизайн двойной слепой: каждый участник приходил дважды с интервалом в неделю. Один раз получал капсулу со 100 мг кофеина (это примерно одна чашка кофе), другой — пустышку с лактозой. Ни участники, ни исследователи не знали, что именно принимается в конкретный день. Перед каждым визитом 6 часов без кофеина, 2 часа без еды, полчаса без физических нагрузок.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/254/62b/266/25462b2669e4998c59d0523f8becefe0.png alt=Источник title=Источник width=571 height=622 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/254/62b/266/25462b2669e4998c59d0523f8becefe0.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/254/62b/266/25462b2669e4998c59d0523f8becefe0.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;a href=https://pubmed.ncbi.nlm.nih.gov/12062222/&gt;Источник&lt;/a&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Учёные специально выбрали именно 100 мг. При 200 мг и выше начинают меняться системные показатели: растёт давление, учащается пульс. И тогда непонятно — кровоток упал, потому что кофеин напрямую действует на сосуды, или просто потому, что изменилось давление во всей системе? При 100 мг таких изменений нет, значит, всё, что происходит с сосудами, — это прямое локальное действие на сосудистую стенку.&lt;p&gt;Измерения делали каждые 15 минут специальной штукой, которая светит лазером в глаз и по характеру отражённого света оценивает движение крови. На выходе — индекс SBR, условная величина, которая показывает скорость кровотока. Параллельно измеряли артериальное давление, пульс и внутриглазное давление.&lt;p&gt;Результат: кровоток в зрительном нерве и в сосудистой оболочке глаза (хориоидея — это слой сосудов под сетчаткой) достоверно снизился. По AUC (это суммарный эффект за два часа, а не одна точка измерения) в зрительном нерве p = 0,0218, в хориоидее-сетчатке p = 0,0469. В среднем по хориоидее на 60-й минуте — минус 6% кровотока. Для сравнения: при двойной дозе в более ранней работе снижение доходило до 13% — зависимость выглядит примерно линейной.&lt;p&gt;Главный сюрприз: артериальное давление — стабильно (примерно 79–80 мм рт. ст. во всех точках), как и пульс (примерно 70 уд/мин). Внутриглазное давление тоже без изменений (около 13 мм рт. ст.). Кровоток упал, а вся система как будто этого не заметила.&lt;p&gt;Учёные посчитали сосудистое сопротивление: взяли перфузионное давление (грубо — давление, которое «проталкивает» кровь через ткани) и разделили на скорость кровотока. Если давление не изменилось, а кровотока стало меньше — значит, сосуды физически сузились. Как шланг: давление воды то же, а струя слабее, потому что его пережали. На 60-й минуте сосудистое сопротивление значимо выросло.&lt;p&gt;Момент минимального кровотока у всех разный. У кого-то на 45-й минуте, у кого-то на 75-й. Это нормальная физиология: скорость, с которой организм перерабатывает кофеин, сильно различается. Период полувыведения (время, за которое концентрация вещества падает вдвое) варьирует от 2,7 до 9,9 часа. Почти четырёхкратная разница. Зависит это от активности фермента CYP1A2 в печени.&lt;p&gt;Вывод: кофеин напрямую сужает сосуды независимо от изменений давления или пульса. Это не побочный эффект общей реакции организма, а прямое действие на сосудистую стенку.&lt;p&gt;Когда он наносится на кожу вокруг глаз, то работает по тому же принципу: сужает капилляры и снижает их проницаемость локально, там, где нанесён. Кровь меньше застаивается, жидкость меньше выходит в ткани, отёк уменьшается.&lt;p&gt;Логичный вопрос: если кофеин не проходит сквозь кожу насквозь, как он вообще влияет на сосуды? Через сигнальные цепочки. Он воздействует на клетки верхнего слоя эпидермиса, а те уже передают сигнал дальше — по принципу «одна клетка говорит другой, что делать».&lt;p&gt;Суровая фармакология и никакого мошенства!&lt;p&gt;&lt;a href=&#34;https://youtu.be/5Y7ZZsOS4O4?si=VR2zINIaDhi09sfF&#34;&gt;Музыкальная пауза &lt;/a&gt;для тех, кто дочитал до середины.&lt;h3&gt;Почему предыдущая формула нас не устраивала&lt;/h3&gt;&lt;p&gt;До геля &lt;a href=https://geltek.ru/catalog/product/gel-dlya-kozhi-vokrug-glaz-caffeine-lift/&gt;Caffeine Lift &lt;/a&gt;у нас уже был продукт с кофеином в роли экстренной помощи и &lt;a href=https://geltek.ru/catalog/product/selective-gel-dlya-kozhi-vokrug-glaz/&gt;базовый гель&lt;/a&gt; для кожи вокруг глаз.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c06/5b7/b61/c065b7b61ff3b487cb4d677945bdfa36.png width=582 height=626 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c06/5b7/b61/c065b7b61ff3b487cb4d677945bdfa36.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c06/5b7/b61/c065b7b61ff3b487cb4d677945bdfa36.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Базовый — он на то и базовый, без акцента на активный дренаж.&lt;p&gt;Часть пользователей этого геля жаловалась на жжение. Это неудивительно: кожа вокруг глаз — самая тонкая на лице, буквально в доли миллиметра, плюс анатомически близко к слизистой. Любой агрессивный компонент там ощущается сразу. Мы пересмотрели систему консервантов и заменили её на более мягкую: каприлгидроксамовую кислоту и Disodium EDTA. Ну и решили докрутить формулу и усилить кофеином, чтобы два раза не вставать.&lt;p&gt;Вторая проблема вскрылась уже на альфа-тестировании: у нас это было 20 человек и 3–4 недели. Мы сравнивали доработанную версию с текущей и выяснили, что эффект по отёкам есть, но слабее, чем хотелось бы. Стали разбирать состав.&lt;p&gt;За противоотёчный эффект отвечал пептид айсерил — это молекула, которая должна улучшать микроциркуляцию и уменьшать застой жидкости. На практике его оказалось недостаточно. В итоге заменили его на дипептид-2 (он лучше работает с лимфодренажем) и добавили дополнительные активы. После этого провели бета-тестирование (больше людей, дольше использование), и там картина уже была стабильной.&lt;p&gt;Концентрация инкапсулированного кофеина в новом геле — 5% (в пересчёте на чистый — 2,5%), но дело не только в количестве, но и в упаковке (капсула).&lt;p&gt;Капсулы удерживают активное вещество и постепенно его высвобождают. В итоге кофеин не лупит сразу всей дозой и не исчезает через короткое время, а работает, пока средство остаётся на коже. В составе комплекса также изониацинамид: он стабилизирует капсулу и улучшает растворимость кофеина в формуле.&lt;p&gt;Для зоны вокруг глаз это решает сразу две задачи: безопасность и эффективность.&lt;p&gt;Вазоактивная часть не ограничивается кофеином. Глюкозил гесперидин (биофлавоноид из кожуры цитрусовых) работает с тем, что кофеин ещё не успел сделать. То есть кофеин разбирается с уже накопившейся жидкостью, а гесперидин снижает вероятность того, что она вообще выйдет из сосудов, укрепляет сосудистую стенку и уменьшает её проницаемость.&lt;p&gt;На антиоксидантной подтанцовке — экстракт петрушки, который нейтрализует свободные радикалы. На осветление работают перамиган с экстрактами одуванчика и корня солодки. Солодка вообще ингибитор тирозиназы, то есть тормозит ключевой фермент, запускающий синтез меланина.&lt;p&gt;Основа геля — вода, глицерин, пропиленгликоль. Последний работает двойным агентом: растворяет компоненты и помогает им проникать в кожу. Молочная кислота здесь не отшелушивает, а регулирует pH формулы, чтобы всё перечисленное выше работало в оптимальных условиях. Гиалуроновая кислота (высокомолекулярная) удерживает влагу на поверхности кожи, чтобы гелевая текстура без масел не давала ощущения сухости.&lt;h3&gt;Куда и как мазать&lt;/h3&gt;&lt;p&gt;Утром и вечером по костному краю (то есть не вплотную к ресницам, а по орбитальной кости). По верхнему веку — от внутреннего угла к внешнему, прямо под бровью. По нижнему — отступив примерно 5 мм от ресничного края, от внешнего угла к внутреннему. Движение в сторону лимфатических узлов механически ускоряет отток жидкости. Если кожа сухая и одного геля недостаточно: утром — гель, вечером — более плотный, укрепляющий крем.&lt;p&gt;На всякий случай, как и с любыми другими средствами, стоит провести тест на чувствительность во избежание.&lt;p&gt;Жировые грыжи он не уберёт, глубокую анатомическую ямку не заполнит — с этим либо смириться, либо отнести хирургу. Генетически выраженные вены тоже никуда не денутся.&lt;p&gt;Но если под глазами фофаны, то это как раз та ниша, под которую и собиралась формула.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/geltek/articles/1030048/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030048</guid>
      <pubDate>Thu, 30 Apr 2026 11:00:19 +0000</pubDate>
    </item>
    <item>
      <title>Как мы переписывали логику очередей: Celery =&amp;gt; aio-pika =&amp;gt; FastStream</title>
      <link>https://habr.com/ru/articles/1030082/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030082</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Наш путь активной работы с очередями RabbitMQ начался с классического Celery. Осознав критичность низкоуровневого контроля системы, принялись работать с aio-pika. Но и этот уровень слишком местами сложный (далее расскажу почему), и нашли отличное решение, на текущий момент, в лице FastStream. Сразу оставлю такую пометку, что каждый инструмент подходит для решения своей задачи. Мы больше хотели сделать акцент на удобство и скорость разработки относительно затрачиваемого времени на миграции решений.&lt;p&gt;&lt;em&gt;N.B.&lt;/em&gt;: Код возможно покажется неоптимальным или старым. Это всё наш дорогой Легаси.&lt;h3&gt;Постановка задачи&lt;/h3&gt;&lt;p&gt;Наша система построена на основе микросервисов, работающих с RabbitMQ. Внутри - обычный асинхронный код для похода на внешние API и в БД.&lt;p&gt;Требования:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Надежный консьюминг - это для нас критично, чтобы сообщение шло по всему флоу и нигде не останавливалось без причин. Если ошибка падает, то это должно отражаться в 3 местах: БД, логи и метрики.&lt;li&gt;&lt;p&gt;Ретраи при ошибках обработки.&lt;li&gt;&lt;p&gt;Трейсинг - поддержка OpenTelemetry.&lt;li&gt;&lt;p&gt;Мониторинг сервиса через healthcheck’и.&lt;li&gt;&lt;p&gt;Prometheus метрики.&lt;/ul&gt;&lt;hr&gt;&lt;h3&gt;Решение №1: Celery как консьюмер&lt;/h3&gt;&lt;h4&gt;Почему Celery&lt;/h4&gt;&lt;p&gt;Celery — классический инструмент для фоновых задач, знакомый большинству Python-разработчиков. Из коробки: декларативное описание задач, ретраи с экспоненциальной задержкой, хранение результатов, мониторинг через Flower, интеграции с фреймворками. Логика проста: пишешь &lt;code&gt;@app.task&lt;/code&gt;, запускаешь воркер — и сообщения из очереди начинают обрабатываться.&lt;h4&gt;Как мы его использовали&lt;/h4&gt;&lt;p&gt;Мы не отправляли задачи из кода в духе &lt;code&gt;my_task.delay()&lt;/code&gt;, а настраивали Celery на прослушивание внешней очереди, куда сообщения попадали от других систем. По сути, Celery выступал как consumer: подключался к брокеру, забирал сообщения, десериализовал и передавал в наши обработчики. Настройки вроде &lt;code&gt;max_retries&lt;/code&gt;, &lt;code&gt;default_retry_delay&lt;/code&gt;, &lt;code&gt;countdown&lt;/code&gt; позволяли гибко управлять поведением при сбоях. Важно ещё подсветить, что результат всегда игнорируется с помощью параметра &lt;code&gt;ignore_result=True&lt;/code&gt; поскольку все результаты записываются в БД.&lt;p&gt;Пример инициализации воркера:&lt;pre&gt;&lt;code class=python&gt;def create_app(&#xA;    name,&#xA;    broker,&#xA;    include,&#xA;    backend=None,&#xA;    task_queues=None,&#xA;    liveness_probe=1,&#xA;    update_period=60,&#xA;    watcher_config={},&#xA;):&#xA;    # Создание само приложение + наложение дополнительных конфигурации&#xA;    app = Celery(name, broker=broker, include=include, backend=backend)&#xA;    app.conf.update(&#xA;        result_expires=120,&#xA;    )&#xA;    add_without_heartbeat_argument = Option(&#xA;        (&amp;#34;--without-heartbeat&amp;#34;,),&#xA;        default=True,&#xA;    )&#xA;&#xA;    app.user_options[&amp;#34;worker&amp;#34;].add(add_without_heartbeat_argument)&#xA;    if task_queues is not None:&#xA;        app.conf.task_queues = task_queues&#xA;&#xA;    if liveness_probe:&#xA;        add_update_period_argument = Option(&#xA;            (&amp;#34;--update-period&amp;#34;,),&#xA;            default=update_period,&#xA;        )&#xA;&#xA;        HEARTBEAT_FILE.touch()&#xA;&#xA;        app.user_options[&amp;#34;worker&amp;#34;].add(add_update_period_argument)&#xA;&#xA;        # Добавление кастомной livenessProbe для K8s&#xA;        app.steps[&amp;#34;worker&amp;#34;].add(LivenessProbe)&#xA;    &#xA;    # инициализация трейсинга&#xA;    with_tracing = watcher_config.get(&amp;#34;with_tracing&amp;#34;)&#xA;    if with_tracing:&#xA;        tracing_exporters = watcher_config.get(&amp;#34;tracing_exporters&amp;#34;, ())&#xA;        signals.worker_process_init.connect(&#xA;            init_celery_tracing(app_name=name, tracing_exporters=tracing_exporters),&#xA;            weak=False,&#xA;        )&#xA;    return app&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:87px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;С чем столкнулись&lt;/h4&gt;&lt;p&gt;Celery проектировался как система передачи сообщений между системами. Из-за чего столкнулись со следующими проблемами:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Потребление памяти&lt;/strong&gt; — из-за оборачивания каждого сообщения в метаданные и хранения внутренних структур Celery расход оперативной памяти быстро рос пропорционально потоку сообщений. При высоком темпе обработки воркер начинал использовать значительно больше ОЗУ, чем требовалось самой бизнес-логике, что вынуждало выделять избыточные ресурсы.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Управление соединениями и heartbeat&lt;/strong&gt; — Celery скрывает многие детали брокера, из‑за чего при сетевых сбоях восстановление происходило с задержками, а тонкая настройка &lt;code&gt;consumer_timeout&lt;/code&gt;, &lt;code&gt;broker_transport_options&lt;/code&gt; была сложной и плохо документированной.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Избыточность&lt;/strong&gt; — Как можно заметить мы используем минимальные дополнительные конфигурации Celery.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Падение контейнера на битом сообщений&lt;/strong&gt; — Будет не совсем справедливо относить это полностью к минусам самого Celery, это больше камень в наш огород. Но опыт есть опыт. Если воркер обрабатывал невалидное сообщение в плане структуры, то падал весь контейнер, при этом сообщение консьюмилось. Что приводило к непониманию “куда делось сообщение и что с ним произошло?”.&lt;/ul&gt;&lt;p&gt;Стало ясно: Celery — отличный выбор для фоновых задач с результатом, но для непрерывного потокового консьюминга он перегружен и недостаточно прозрачен.&lt;hr&gt;&lt;h3&gt;Решение №2: aio-pika — близко к железу&lt;/h3&gt;&lt;h4&gt;Идея&lt;/h4&gt;&lt;p&gt;Перейти на чистый AMQP, отказавшись от посредника в виде Celery. aio-pika — асинхронная библиотека для RabbitMQ, предоставляющая прямой доступ к каналам, обменникам, очередям. Код становится минимальной прослойкой над протоколом: сами управляем подписками, подтверждениями (ack/nack), префетчем, реконнектами.&lt;h4&gt;Что переписали&lt;/h4&gt;&lt;p&gt;Написали небольшой собственный “фреймворк” консьюминга: асинхронный раннер, который при запуске создаёт соединение, открывает канал, объявляет очереди с нужными параметрами durable/exclusive, подписывается на них, а в колбэке вызывает наши обработчики.&lt;p&gt;Поверх этого появились:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Ручной retry&lt;/strong&gt; — Декоратор поверх колбэка, имеющий свой счётчик кол-во повторов при возникновений ошибок.&lt;li&gt;&lt;p&gt;&lt;strong&gt;LivenessProb&lt;/strong&gt; — Кастомный на уровне ядра aio-pika представлен чуть ниже.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Трейсинг&lt;/strong&gt; — вручную оборачиваем вызовы в OpenTelemetry спаны, передаём trace context в заголовках AMQP при ретраях.&lt;/ul&gt;&lt;p&gt;Пример LivenessProb:&lt;pre&gt;&lt;code class=python&gt;class CustomRobustConnection(RobustConnection):&#xA;    def __init__(&#xA;        self,&#xA;        url: URL,&#xA;        loop: asyncio.AbstractEventLoop | None = None,&#xA;        **kwargs: Any,&#xA;    ):&#xA;        super().__init__(url=url, loop=loop, **kwargs)&#xA;        # Кастомный класс пробы, схож с тем, что выше для Celery&#xA;        self._liveness_probe = LivenessProbe()&#xA;&#xA;    async def connect(self, timeout: TimeoutType = None) -&amp;gt; None:&#xA;        # Тут держитесь крепче. Я когда впервые это увидел, без литра кофе не смог понять как оно работает&#xA;        self._RobustConnection__connect_timeout = timeout&#xA;&#xA;        if self.is_closed:&#xA;            raise RuntimeError(f&amp;#34;{self!r} connection closed&amp;#34;)&#xA;&#xA;        if self.reconnecting:&#xA;            raise RuntimeError(&#xA;                (&#xA;                    &amp;#34;Connect method called but connection &amp;#34;&#xA;                    f&amp;#34;{self!r} is reconnecting right now.&amp;#34;&#xA;                ),&#xA;                self,&#xA;            )&#xA;&#xA;        if not self._RobustConnection__reconnection_task:&#xA;            self._RobustConnection__reconnection_task = self.loop.create_task(&#xA;                self.__connection_factory(),&#xA;            )&#xA;&#xA;        await self._RobustConnection__fail_fast_future&#xA;        await self.connected.wait()&#xA;&#xA;    async def __connection_factory(self) -&amp;gt; None:&#xA;        logger.debug(&amp;#34;Starting connection factory for %r&amp;#34;, self)&#xA;        while not self.is_closed and not self._close_called:&#xA;            logger.debug(&amp;#34;Waiting for connection close event for %r&amp;#34;, self)&#xA;            await self._RobustConnection__connection_close_event.wait()&#xA;&#xA;            if self.is_closed or self._close_called:&#xA;                return&#xA;&#xA;            try:&#xA;                self.transport = None&#xA;                self.connected.clear()&#xA;&#xA;                logger.debug(&amp;#34;Connection attempt for %r&amp;#34;, self)&#xA;                await Connection.connect(self, self._RobustConnection__connect_timeout)&#xA;&#xA;                if not self._RobustConnection__fail_fast_future.done():&#xA;                    self._RobustConnection__fail_fast_future.set_result(None)&#xA;&#xA;                logger.debug(&amp;#34;Connection made on %r&amp;#34;, self)&#xA;                self._liveness_probe.start()&#xA;&#xA;            except CONNECTION_EXCEPTIONS as e:&#xA;                if not self._RobustConnection__fail_fast_future.done():&#xA;                    self._RobustConnection__fail_fast_future.set_exception(e)&#xA;                    return&#xA;&#xA;                logger.warning(&#xA;                    &amp;#39;Connection attempt to &amp;#34;%s&amp;#34; failed: %s. &amp;#39;&#xA;                    &amp;#34;Reconnecting after %r seconds.&amp;#34;,&#xA;                    self,&#xA;                    e,&#xA;                    self.reconnect_interval,&#xA;                )&#xA;                self._liveness_probe.stop()&#xA;            except Exception:&#xA;                logger.exception(&#xA;                    &amp;#34;Reconnect attempt failed %s. &amp;#34; &amp;#34;Retrying after %r seconds.&amp;#34;,&#xA;                    self,&#xA;                    self.reconnect_interval,&#xA;                )&#xA;                self._liveness_probe.stop()&#xA;&#xA;            await asyncio.sleep(self.reconnect_interval)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;h4&gt;Что стало лучше&lt;/h4&gt;&lt;p&gt;Полный контроль над жизненным циклом соединения, тонкая настройка prefetch, возможность реализовать любую логику подтверждения (например, отложенный ack после завершения цепочки действий). Нет лишних метаданных в теле сообщения — брокер передаёт ровно то, что отправил продюсер. Асинхронность нативная, работает на asyncio без костылей.&lt;h4&gt;Проблемы&lt;/h4&gt;&lt;p&gt;Каждая «плюшка» делалась вручную и со временем объём инфраструктурного кода разросся. Десятки строк для декларации очередей, логирование reconnect‑цикла, согласование формата trace‑заголовков между сервисами. Healthcheck, хоть и был создан вручную, требовал аккуратности: нужно было отслеживать состояние не только TCP-соединения, но и открытого канала.&lt;hr&gt;&lt;h3&gt;Решение №3: FastStream — золотая середина&lt;/h3&gt;&lt;h4&gt;Основная идея&lt;/h4&gt;&lt;p&gt;FastStream — надстройка над aio-pika (а также над NATS, Kafka), которая даёт декларативный стиль описания consumer’ов, lifespan‑хуки, встроенные механизмы: healthcheck-эндпоинт, OpenTelemetry-интеграция, метрики Prometheus.&lt;p&gt;По сути, это aio-pika, обёрнутая в лучшие практики, которые мы сами реализовывали руками в предыдущем решении. Механизм retry описан как пример использования middleware.&lt;h4&gt;Что пошло так&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Healthcheck из коробки&lt;/strong&gt; — достаточно указать &lt;code&gt;FastStream&lt;/code&gt; объект в ASGI-приложении (через &lt;code&gt;AsgiFastStream&lt;/code&gt;), и на &lt;code&gt;/health&lt;/code&gt; возвращается статус брокера.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Lifespan&lt;/strong&gt; — менеджер контекста управляет запуском и корректной остановкой consumer’ов, повторными соединениями. Не нужно писать свои обработчики сигналов. Это касалось не только RMQ коннектов, но и например коннектов к базам данных.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Мониторинг и трейсинг&lt;/strong&gt; — подключение OpenTelemetry сводится к нескольким строчкам: спаны автоматически создаются для каждого обработанного сообщения, propagate context через заголовки.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Декларативные middleware&lt;/strong&gt; — проще внедрять кросс‑касательную логику (логирование, валидацию) без захламления бизнес-кода.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Простота конфигурации&lt;/strong&gt; — брокер, очереди, обменники описываются через Python-декораторы и типы, нет нужды вручную управлять каналами и ack/nack.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Интеграция с Pydantic&lt;/strong&gt; — Сообщение на уровне описание консьюмеров сразу пытается отвалидироваться в Pydantic модель, если это нужно.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dependency Injection&lt;/strong&gt; — Как можно увидеть в примере ниже &lt;code&gt;FastStream&lt;/code&gt; предлагает нам возможность подключение DI как в &lt;code&gt;FastAPI&lt;/code&gt;.&lt;/ul&gt;&lt;p&gt;Вот во что превратилась кодовая база одной инициализации процесса:&lt;pre&gt;&lt;code class=python&gt;app = AsgiFastStream(&#xA;    broker,&#xA;    # health + metrics из коробки&#xA;    asgi_routes=[&#xA;        (&#xA;            &amp;#34;/health&amp;#34;,&#xA;            make_system_ping_asgi(broker, timeout=5.0, include_in_schema=False),&#xA;        ),&#xA;        (&amp;#34;/metrics&amp;#34;, make_asgi_app(registry)),&#xA;    ],&#xA;    # Кастомный lifespan. Обычно здесь идёт инициализации подключений к БД&#xA;    lifespan=lifespan,&#xA;)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Было бы ещё не очень справедливо не показать, что из себя представляет broker&lt;pre&gt;&lt;code class=python&gt;broker = RabbitBroker(&#xA;    settings.RMQ_URL,&#xA;    # Примеры миддлвары&#xA;    # RabbitPrometheusMiddleware из коробки для мониторинга сообщений&#xA;    # EnrichLogMiddleware и FailCatchComplexMiddleware кастомные для отслеживания логирования и ошибок &#xA;    middlewares=(&#xA;        RabbitPrometheusMiddleware(registry=registry),&#xA;        EnrichLogMiddleware,&#xA;        FailCatchComplexMiddleware(&#xA;            ignore_routing_keys=[&#xA;                settings.RMQ_FAIL_TABLE_QUEUE,&#xA;                settings.RMQ_DASHBOARD_SETTINGS_QUEUE,&#xA;                settings.RMQ_TIMEOUT_QUEUE,&#xA;            ],&#xA;        ),&#xA;    ),&#xA;    logger=logger,&#xA;    # Кастомные парсеры и декодеры для обработки входящих сообщений&#xA;    parser=json_parser,&#xA;    decoder=decoder,&#xA;)&#xA;&#xA;broker.include_router(router)&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;и пример как инициализируется консьюмер&lt;pre&gt;&lt;code class=python&gt;from faststream import Depends&#xA;from faststream.rabbit import (&#xA;    ExchangeType,&#xA;    RabbitExchange,&#xA;    RabbitMessage,&#xA;    RabbitQueue,&#xA;    RabbitRouter,&#xA;)&#xA;from faststream.rabbit.annotations import RabbitBroker as ContextRabbitBroker&#xA;&#xA;# Вместо RabbitRouter можно использовать broker.&#xA;router = RabbitRouter()&#xA;@router.subscriber(&#xA;    RabbitQueue(&#xA;        name=...,&#xA;        durable=True,&#xA;        routing_key=...,&#xA;    ),&#xA;    RabbitExchange(&#xA;        name=...,&#xA;        durable=True,&#xA;    ),&#xA;)&#xA;async def on_service_hub_message(&#xA;    message: RabbitMessage,&#xA;    # Имеется общий контекст всей системы&#xA;    broker: ContextRabbitBroker,&#xA;    # DI&#xA;    async_session=Depends(get_db_session),&#xA;) -&amp;gt; None:&#xA;&lt;/code&gt;&lt;div class=code-explainer&gt;&lt;a href=https://sourcecraft.dev/ class=&#34;tm-button code-explainer__link&#34; style=&#34;visibility: hidden;&#34;&gt;&lt;img style=width:14px;height:14px;object-fit:cover;object-position:left;&gt;&lt;/a&gt;&lt;/div&gt;&lt;/pre&gt;&lt;p&gt;Подключение консьюмеров простое, почти схожее с Celery, но чуть шире. Возможности &lt;code&gt;FastStream&lt;/code&gt; на этом не заканчиваются. Если копать ещё глубже, то там можно найти документацию AsyncAPI и In-memory тесты.&lt;h4&gt;Что пошло не так&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Производительность&lt;/strong&gt; — из-за дополнительных слоёв абстракции (middleware, автоматическая обвязка спанов, встроенные ретраи) пропускная способность ниже, чем у голого aio-pika. Это для нас лишь только в теории, поскольку поток не достигает больших значений 30-40 RPS. Справедливости ради, это не относится к минусам, поскольку бенчмарки я не проводил, и фраза “поверь мне брат” меня убедила.&lt;li&gt;&lt;p&gt;&lt;strong&gt;Сложность отслеживания кодовой базы&lt;/strong&gt; — Тут опять из-за дополнительных слоёв абстракции иногда заходишь внутрь посмотреть что там, и можно с лёгкостью потеряться.&lt;/ul&gt;&lt;h4&gt;Итоговое сравнение&lt;/h4&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;th&gt;&lt;p align=left&gt;Критерий&lt;th&gt;&lt;p align=left&gt;Celery&lt;th&gt;&lt;p align=left&gt;aio-pika (самописный)&lt;th&gt;&lt;p align=left&gt;FastStream&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Подход&lt;td&gt;&lt;p align=left&gt;Динамические задачи&lt;td&gt;&lt;p align=left&gt;Низкоуровневый AMQP&lt;td&gt;&lt;p align=left&gt;Высокоуровневый consumer&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Асинхронность&lt;td&gt;&lt;p align=left&gt;Ограниченная (gevent)&lt;td&gt;&lt;p align=left&gt;Полная (asyncio)&lt;td&gt;&lt;p align=left&gt;Полная (asyncio)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Healthcheck&lt;td&gt;&lt;p align=left&gt;Требует доп. решения&lt;td&gt;&lt;p align=left&gt;Ручная реализация&lt;td&gt;&lt;p align=left&gt;Из коробки&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Retry&lt;td&gt;&lt;p align=left&gt;Встроен в задачу&lt;td&gt;&lt;p align=left&gt;Реализуется вручную&lt;td&gt;&lt;p align=left&gt;Реализуется вручную (есть пример)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Трейсинг (OTel)&lt;td&gt;&lt;p align=left&gt;Через сигналы Celery&lt;td&gt;&lt;p align=left&gt;Ручное встраивание&lt;td&gt;&lt;p align=left&gt;Из коробки&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Контроль&lt;td&gt;&lt;p align=left&gt;Низкий&lt;td&gt;&lt;p align=left&gt;Максимальный&lt;td&gt;&lt;p align=left&gt;Средний (middleware)&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Производительность&lt;td&gt;&lt;p align=left&gt;Умеренная&lt;td&gt;&lt;p align=left&gt;Высокая&lt;td&gt;&lt;p align=left&gt;Больше чем у Celery, ниже чем у aio-pika&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;Кривая входа&lt;td&gt;&lt;p align=left&gt;Низкая&lt;td&gt;&lt;p align=left&gt;Средняя (требует знаний AMQP)&lt;td&gt;&lt;p align=left&gt;Низкая (знакомо по FastAPI)&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;hr&gt;&lt;h3&gt;Заключение&lt;/h3&gt;&lt;p&gt;Пройдя путь от монолитного Celery через хрупкий самописный код до сбалансированного FastStream, мы пришли к инструменту, который закрывает все эксплуатационные потребности: healthcheck, трейсинг, метрики — и при этом не требует поддерживать сотни строк инфраструктурного кода.&lt;p&gt;Да, возможно я выступаю в качестве адвоката этого инструмента, но что могу поделать, когда он так понравился? это вы ещё не увидели как стараюсь переписать всё на Rust.&lt;p&gt;Отойдя от жаргона и шуток повторю первую свою мысль: У каждой задачи свой инструмент. Мой — это FastStream.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>GregTMJ</author>
      <guid>https://habr.com/ru/articles/1030082/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030082</guid>
      <pubDate>Thu, 30 Apr 2026 10:58:52 +0000</pubDate>
    </item>
    <item>
      <title>Фишинг в 2026: как ИИ и дипфейки изменили методы атак, но не изменили способы защиты</title>
      <link>https://habr.com/ru/articles/1030080/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030080</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;h4&gt;Вступление&lt;/h4&gt;&lt;p&gt;Привет, Хабр! Находясь на мероприятии &lt;a href=http://BI.ZONE rel=&#34;noopener noreferrer nofollow&#34;&gt;BI.ZONE&lt;/a&gt; Day, я слушал доклады и ходил между стендами, думая, чтобы такого спросить, не связанного с продуктами и технологиями компании, чьё было мероприятие. И тут меня привлекла презентация про фишинг, я не мог отказать себе в удовольствии задать несколько вопросов специалистам по фишингу. Тем более тема всегда актуальная и интересная. Поэтому я поговорил с руководителем &lt;a href=http://BI.ZONE rel=&#34;noopener noreferrer nofollow&#34;&gt;BI.ZONE&lt;/a&gt; AntiFraud Алексеем Лужновым и руководителем &lt;a href=http://BI.ZONE rel=&#34;noopener noreferrer nofollow&#34;&gt;BI.ZONE&lt;/a&gt; Digital Risk Protection Дмитрием Кирюшкиным. Встречайте интервью Алексея и Дмитрия о том, как ИИ изменил методы фишинга, какие приёмы социальной инженерии до сих пор работают в 2026 году, насколько велик рынок понятия «фишинга как услуги» (Phishing as a Service, PHaaS) и не только. Приятного чтения!&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f67/2e8/e5f/f672e8e5f1918c5a659c7ed0e5bc82ac.webp width=1672 height=941 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f67/2e8/e5f/f672e8e5f1918c5a659c7ed0e5bc82ac.webp 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f67/2e8/e5f/f672e8e5f1918c5a659c7ed0e5bc82ac.webp 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h4&gt;Интервью&lt;/h4&gt;&lt;p&gt;&lt;strong&gt;Как ИИ (нейросети, дипфейки) изменил методы фишинга в 2026 году?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Алексей Лужнов&lt;/strong&gt;: В 2026 году ИИ-инструменты заметно повлияли на фишинг. Атаки стали более персонализированными. Если раньше рассылки были типовыми, то теперь злоумышленники создают сообщения под конкретного человека: с учетом его интересов, работы, окружения.&lt;p&gt;Мошенники все чаще используют дипфейки и подделывают голос, внешность реальных людей, чтобы звучать убедительнее. Это помогает быстрее войти в доверие и повышает шансы на успех атаки. Злоумышленники продумывают сценарии обмана, подбирая то, на что человек с большей вероятностью отреагирует. Чаще всего они выдают себя за близких, которые якобы попали в сложную ситуацию, и просят срочно перевести деньги.&lt;p&gt;&lt;strong&gt;Какие каналы (электронная почта, мессенджеры, соцсети, SMS и телефонные звонки) чаще всего используют злоумышленники для фишинга?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Дмитрий Кирюшкин&lt;/strong&gt;: Злоумышленники отслеживают пользовательскую активность и задействуют те каналы, которые потенциальные жертвы используют чаще всего. Как правило, это социальные сети и мессенджеры. На этих платформах мошенники создают тематические каналы и чаты, где затем размещают фишинговый контент. Они активно распространяют ссылки на мошеннические ресурсы в комментариях к постам популярных каналов, а также связываются с пользователями через личные сообщения. Чаще всего злоумышленники стремятся получить авторизационные данные от аккаунтов в государственных сервисах или мессенджерах, а также информацию о банковских счетах и другие чувствительные данные жертв.&lt;p&gt;При этом традиционным способом доставки фишингового контента остается электронная почта. По данным исследования Threat Zone 2026, 64% целевых атак на компании России и других стран СНГ начинаются именно с фишинговых писем. С помощью них атакующие стремятся получить доступ к IT-инфраструктуре организаций: достаточно одного открытого вложения, чтобы злоумышленники могли скомпрометировать устройство и получить доступ к чувствительным данным компании. &lt;p&gt;Телефонные и видеозвонки позволяют киберпреступникам повлиять на эмоциональное состояние пользователей. Злоумышленники оказывают давление, ограничивают время для принятия решения, чтобы жертва потеряла концентрацию и начала выполнять требования мошенников. Таким образом, шансы на успешный обман увеличиваются.&lt;p&gt;&lt;strong&gt;Какие сектора (финансы, промышленность, медицина, государственные учреждения) оказались наиболее подвержены фишинговым атакам?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Дмитрий Кирюшкин&lt;/strong&gt;: Примерно с октября 2025 года мы отмечаем, что большинство фишинговых атак нацелено на получение доступа к мессенджерам или личным кабинетам в государственных сервисах. Также мы наблюдаем рост количества атак типа Ghost invoice. Это не совсем типичный фишинг, но близкое к этому виду мошенничество. При такой схеме злоумышленники создают домены, похожие по наименованию на названия ресурсов крупных компаний: чаще всего они связаны с промышленной сферой. От имени этих организаций рассылают предложения потенциальным клиентам. По незнанию они могут оплатить поддельные счета и остаться без денег и без товара. При этом компания, которую имитируют мошенники, потеряет доверие клиентов и репутацию. &lt;p&gt;&lt;strong&gt;Какие приёмы социальной инженерии (фальшивые письма от банков, уведомления от госслужб, лотереи и т.д.) наиболее эффективны в рамках фишинга в 2026 году?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Алексей Лужнов&lt;/strong&gt;: Одним из самых работающих сценариев остаются фейковые уведомления от банков, налоговых служб, государственных сервисов. Они эффективны потому, что воспринимаются как критически важные сервисы, из-за чего у людей возникает тревога и снижается критичность восприятия. Пользователи склонны быстро реагировать на сообщения о задолженностях, блокировке счетов или штрафах. К тому же такие письма часто приходят в момент, когда жертва уже ожидает уведомления: например, в период оплаты налогов. Бдительность снижается, и возрастает вероятность импульсивного действия. &lt;p&gt;Большой популярностью все еще пользуется схема Fake Boss, при которой злоумышленники выдают себя за руководителя компании. Сотруднику пишут якобы от имени начальника, предупреждают о предстоящем звонке от правоохранительных органов или регуляторов и просят посодействовать. Затем жертву убеждают под угрозами проверок перевести деньги на «безопасный счет», который контролируют мошенники. В результате под давлением сотрудники совершают переводы или раскрывают конфиденциальную информацию, что приводит к финансовым потерям и рискам для компании.&lt;p&gt;&lt;strong&gt;Как злоумышленники используют новые инструменты (QR-коды, поддельные мобильные приложения, фишинговые сайты) для проведения атак?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Дмитрий Кирюшкин&lt;/strong&gt;: Мошенники крайне активно используют подобные инструменты для обмана пользователей. Однако некоторые из них постепенно теряют свою популярность. Например, за первый квартал 2026 года обнаружено 12,5 тысяч мошеннических сайтов во всех доменных зонах, включая российскую. За аналогичный период прошлого года таких ресурсов насчитывалось 17,5 тысяч Такая динамика отчасти связана с тем, что регистраторы, хостинг‑провайдеры и команды реагирования оперативно их блокируют. Кроме того, мошенникам все сложнее реализовывать сценарии атак, поскольку общая киберграмотность населения растет из года в год.&lt;p&gt;При этом мы фиксируем интерес злоумышленников к QR-кодам. Как правило, мошенники размещают их в оживленных местах, где пользователи с большей вероятностью обратят на них внимание. Чаще всего такие QR-коды сопровождаются просьбой перейти на указанный ресурс и оставить данные. Мошенники могут использовать указанную информацию для получения доступа к мессенджерам и сервисам или украсть деньги, если пользователь оставил данные банковской карты.&lt;p&gt;Злоумышленники также разрабатывают фишинговые мобильные приложения, которые имитируют оригинальные. Они рассчитывают на невнимательность и спешку пользователей при скачивании. После загрузки такое приложение может собирать информацию об устройстве, авторизационные данные от различных приложений и т. Д. &lt;p&gt;&lt;strong&gt;Что известно о развитии «фишинга как сервиса» (Phishing-as-a-Service) и насколько широко он используется преступниками?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Дмитрий Кирюшкин&lt;/strong&gt;: PhaaS-платформы уже давно превратили создание фишинговых атак в полноценную индустрию. Современные платформы дают мошенникам возможность обхода различных MFA и создания страниц на основе определенного поведения пользователей.&lt;p&gt;Ранее у PHaaS не было аналогов. С появлением ИИ «качественный» фишинговый контент можно создавать за пару запросов. Связка ИИ и PHaaS-платформы позволяет увеличить скорость, простоту и «качество» создания фишинговых ресурсов, что несет еще больший риск для потенциальных жертв. Однако не стоит забывать, что ИИ также помогает совершенствовать киберзащиту.&lt;p&gt;&lt;strong&gt;Какие технологии защиты (антиспам и антифишинговые решения, двухфакторная аутентификация, обучение пользователей) наиболее актуальны?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Алексей Лужнов&lt;/strong&gt;: Пользователям рекомендуем придерживаться базовых правил безопасности: обращаться только к официальным и проверенным источникам, а любые подозрительные сообщения и ссылки дополнительно проверять через другие каналы связи. Важно сохранять внимательность: фейковые аудио и видео могут выдать себя деталями — низким качеством записи, неестественной интонацией или странной мимикой. Кроме того, стоит включить двухфакторную аутентификацию с получением кода через специальные приложения. &lt;p&gt;Организациям стоит инвестировать в решения для выявления дипфейков, использовать защищенные каналы связи и внедрять дополнительные проверки в критических процессах. Например, это принцип «второй руки», когда любую операцию подтверждает доверенное лицо. Важно быстро реагировать на инциденты и регулярно повышать уровень киберграмотности сотрудников. &lt;p&gt;&lt;strong&gt;Какие примеры многоэтапных фишинговых схем («письмо + звонок», цепочки переадресации и т.п.) становятся заметными?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Алексей Лужнов&lt;/strong&gt;: Мы видим, что мошенники смещают фокус с массовых схем на более сложные и многоэтапные. Такие атаки требуют больше подготовки, но позволяют сохранять высокий уровень прибыли с жертв, поэтому используются все чаще.&lt;p&gt;Например, один из сценариев выглядит так: жертве поступает «ошибочный» перевод, после чего ее начинают запугивать. Мошенники утверждают, что счет якобы связан с незаконной деятельностью. Под этим давлением человека убеждают перевести деньги на другие счета или вернуть средства наличными через курьера.&lt;p&gt;Кроме того, распространен метод TOAD (telephone-oriented attack delivery, фишинг с обратным звонком). Злоумышленники заставляют жертву самой выйти с ними на связь, например, через письмо или сообщение с просьбой перезвонить. Благодаря этому обходятся антиспам-фильтры, которые обычно срабатывают при входящем звонке от мошенника, тогда как в таких сценариях инициатива формально исходит от самой жертвы. У человека возникает больше доверия к разговору. Во время такого звонка злоумышленники могут представляться сотрудниками компаний, выманивать логины и пароли или убеждать установить вредоносное приложение. В результате они получают доступ к устройству, аккаунтам жертвы или даже к корпоративным системам.&lt;p&gt;Злоумышленники часто используют СМС-бомбинг как первый этап более сложного сценария. После серии сообщений жертве звонят якобы из банка или сервиса и предлагают срочно «решить проблему», которая связана с якобы подозрительной операцией. Такие схемы могут применяться при попытках получить доступ к личному кабинету или оформить кредит. Мошенники также могут прислать СМС с просьбой перезвонить. В этом случае жертва сама инициирует контакт, и это снижает подозрения, повышает доверие к собеседнику.&lt;p&gt;&lt;strong&gt;Что известно о теневой экономике фишинга: на сколько велик рынок «фишинга как услуги» и какова его доходность?&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Дмитрий Кирюшкин&lt;/strong&gt;: В российской доменной зоне часть фишинга работает по типу PHaaS. Это можно отследить на примере известной схемы «Мамонт». Есть люди, которые создают целую сеть фишинговых доменов, и есть воркеры, которые распространяют фишинговые ссылки за определенный процент от украденных средств. Порог вхождения очень низкий, для этого не нужно обладать глубокими экспертными знаниями. При этом ущерб от таких схем довольно масштабный: по нашей оценке, за 2025 год мошенники похитили более 18 млрд рублей.&lt;h4&gt;Заключение&lt;/h4&gt;&lt;p&gt;Как стало понятно из разговора, фишинг в 2026 году эволюционировал далеко за рамки простых массовых рассылок. Благодаря ИИ, дипфейкам и платформам атаки стали персонализированными, многоэтапными и технологически изощрёнными.&lt;p&gt;Однако технологии развиваются с обеих сторон. И у защиты есть что ответить: двухфакторная аутентификация, антифишинговые решения и обучение пользователей остаются актуальными, но требуют постоянного обновления подходов.&lt;p&gt;Ну что же, обычным пользователям и компаниям остаётся только быть настороже, не терять бдительность и помнить: если что‑то выглядит подозрительно — лучше перепроверить через официальные каналы. Спасибо за прочтение!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>Lexx_Nimofff</author>
      <guid>https://habr.com/ru/articles/1030080/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030080</guid>
      <pubDate>Thu, 30 Apr 2026 10:57:47 +0000</pubDate>
    </item>
    <item>
      <title>Откуда в YADRO конструктивизм: ищем ответы в истории авангарда</title>
      <link>https://habr.com/ru/companies/yadro/articles/1030070/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030070</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;TATLIN, VEGMAN и KORNFELD — это не только идеальное наполнение для центра обработки данных, но и плеяда знаменитых на весь мир архитекторов-конструктивистов эпохи авангарда. Их философией вдохновлялись основатели YADRO, а принцип «функция определяет форму» лег в основу продуктов компании. Но чем занимались архитекторы, в честь которых назвали серверы, СХД и коммутаторы? Какое наследие они оставили нынешнему поколению инженеров? &lt;p&gt;На первый вопрос ответим под катом — разберемся, какие изобретения и проекты конструктивистов считаются самыми значимыми. А ответ на второй вопрос вы узнаете &lt;a href=&#34;https://yadro-engineer-history.ru/?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;на экскурсии «Вычисляя архитектуру»&lt;/a&gt; — коллаборации YADRO и креативного бюро «Глазами инженера». Заодно посмотрите на здание-вычислительный центр, пропорциональное диаметру Земли, башни-радиолампы и телескопический портал в подземный мир. Билеты на майские прогулки уже в продаже.&lt;h3&gt;Владимир Татлин — визионер с инженерным мышлением&lt;/h3&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/2b0/249/b36/2b0249b36329c424c8033118db234882.png alt=&#34;И любитель бандуры&#34; title=&#34;И любитель бандуры&#34; width=780 height=534 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/2b0/249/b36/2b0249b36329c424c8033118db234882.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/2b0/249/b36/2b0249b36329c424c8033118db234882.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;И любитель бандуры&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;В линейке YADRO:&lt;/strong&gt; &lt;a href=&#34;https://yadro.com/ru/tatlin?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;системы хранения данных TATLIN&lt;/a&gt;.&lt;p&gt;&lt;strong&gt;Что вдохновляет:&lt;/strong&gt; визионерство и стремление к совершенству.&lt;p&gt;Об изобретениях Владимира Татлина вы наверняка слышали, хотя далеко не все они дошли до наших дней, а некоторые проекты так и не реализовались. Татлин успел попробовать себя в разных сферах и одним из первых среди авангардистов начал применять свои принципы к предметам повседневного обихода. Он спроектировал орнитоптер «Летатлин», разработал одежду «Нормаль» и множество инновационных для своего времени предметов быта. &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/f9f/f2d/e37/f9ff2de375e133a06e3a6223348c7d47.png alt=&#34;Летательный аппарат «Летатлин» хранится в Третьяковской галерее&#34; title=&#34;Летательный аппарат «Летатлин» хранится в Третьяковской галерее&#34; width=960 height=575 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/f9f/f2d/e37/f9ff2de375e133a06e3a6223348c7d47.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/f9f/f2d/e37/f9ff2de375e133a06e3a6223348c7d47.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Летательный аппарат «Летатлин» хранится в Третьяковской галерее&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В начале карьеры он преподавал во ВХУТЕМАС — главной архитектурной школе своего времени. &lt;blockquote&gt;&lt;p&gt;ВХУТЕМАС —  первая точка маршрута &lt;a href=&#34;https://yadro-engineer-history.ru/?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;«Вычисляя архитектуру»&lt;/a&gt; и место учебы множества авангардистов в 20-е годы прошлого века, когда этот стиль преобладал в искусстве и архитектуре. Но признаки и принципы авангарда можно найти в постройках разных периодов: от от Вычислительного центра Госплана до высотки на Красных воротах. Вместе с опытным гидом из креативного бюро «Глазами инженера» вы узнаете:&lt;ul&gt;&lt;li&gt;&lt;p&gt;как создавались неочевидные инженерные изобретения: например, адимарипы и нейтрализующие стены;&lt;li&gt;&lt;p&gt;почему «дом-компьютер» пропорционален диаметру Земли;&lt;li&gt;&lt;p&gt;где находится несуществующий проспект, на который выходят парадные фасады министерств;&lt;li&gt;&lt;p&gt;зачем инженеры замораживали землю под сталинской высоткой.&lt;/ul&gt;&lt;p&gt;Экскурсии пройдут в Москве 11 и 30 мая в 12:00. Старт маршрута — у Дома Юшкова на «Чистых прудах».&lt;p&gt;&lt;a href=&#34;https://yadro-engineer-history.ru/?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;Купить билет →&lt;/a&gt;&lt;/blockquote&gt;&lt;p&gt;К сожалению, «Башню Третьего интернационала», свой главный проект, Владимиру Татлину так и не удалось воплотить. «Башня Татлина» опередила свое время, став предвестником кинетической архитектуры — зданий, элементы которых могут двигаться относительно друг друга. Однако элементы конструкции башни встречаются в работах другого инженера, Владимира Шухова. Гиперболоид Шухова тоже можно рассмотреть поближе &lt;a href=&#34;https://yadro-engineer-history.ru/?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;на экскурсии «Вычисляя архитектуру»&lt;/a&gt;.&lt;p&gt;&lt;a href=https://engineer.yadro.com/article/kontrrelefy-ornitoptery-i-kineticheskie-bashni-kak-avangardist-tatlin-proektiroval-novyj-mir/&gt;Узнать больше →&lt;/a&gt;&lt;h3&gt;Георгий Вегман — инженер-художник&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;В линейке YADRO:&lt;/strong&gt; &lt;a href=&#34;https://yadro.com/ru/vegman?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;серверы VEGMAN R&lt;/a&gt;.&lt;p&gt;&lt;strong&gt;Что вдохновляет:&lt;/strong&gt; умение смотреть на продукт с разных сторон.&lt;p&gt;Георгий Вегман обучался и во ВХУТЕМАС, и в Московском институте гражданских инженеров. Этот опыт отразился на каждом его проекте: художественное видение и инженерное мышление позволяли архитектору создавать многофункциональные и технологичные здания. &lt;p&gt;Так, Вегман спроектировал ночлежный дом нового типа — компактное здание с логикой поточного движения жильцов, продуманным санитарным блоком, системой вентиляции и обогрева. Архитектурное решение отличалось строгой функциональностью планировки: спальные помещения разделялись на блоки по 25−30 человек с обязательными санитарно-гигиеническими узлами, общей кухней и прачечной.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/ad3/f90/26f/ad3f9026fa6e6893fe7c6128c8dfadad.png alt=&#34;Проект ночлежного дома&#34; title=&#34;Проект ночлежного дома&#34; width=900 height=424 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/ad3/f90/26f/ad3f9026fa6e6893fe7c6128c8dfadad.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/ad3/f90/26f/ad3f9026fa6e6893fe7c6128c8dfadad.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Проект ночлежного дома&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Инженерное нововведение Вегмана на этой стройке — сборные стандартизированные конструкции, применение которых значительно сократило сроки строительства. Систему вентиляции Вегман спроектировал с учетом повышенной плотности размещения людей и сделал ее приточно-вытяжной с подогревом воздуха в зимний период. Для конструктивиста это было не просто здание, а социальная машина, решающая задачи гигиены, управления потоками людей, эксплуатации и комфорта.&lt;p&gt;&lt;a href=https://engineer.yadro.com/article/arhitektor-vegman-i-konstruktivisty/&gt;Узнать больше →&lt;/a&gt;&lt;h3&gt;Яков Корнфельд — проектировщик зрелищных зданий&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;В линейке YADRO: &lt;/strong&gt;&lt;a href=&#34;https://yadro.com/ru/kornfeld/?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;сетевые&lt;strong&gt; &lt;/strong&gt;коммутаторы KORNFELD&lt;/a&gt;.&lt;p&gt;&lt;strong&gt;Что вдохновляет: &lt;/strong&gt;идея создать универсальный продукт в своей сфере.&lt;p&gt;Самый известный проект Якова Корнфельда — кинотеатр «Родина» в Москве, и его уникальность в том, что он не стремится быть уникальным. Корнфельд спроектировал типовой советский кинотеатр, аналоги «Родины» есть по всей стране. Тем не менее в своем «массовом» решении конструктивист все-таки отстроился от главного зрелищного здания начала XX века — монументального, но неадаптивного кинотеатра «Художественный». &lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/c23/570/44d/c2357044d8a81db08117fed273cb1160.png alt=&#34;«Родина» в 1952 году&#34; title=&#34;«Родина» в 1952 году&#34; width=800 height=594 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/c23/570/44d/c2357044d8a81db08117fed273cb1160.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/c23/570/44d/c2357044d8a81db08117fed273cb1160.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;«Родина» в 1952 году&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Идея Корнфельда заключалась в том, чтобы создать универсальный проект — архитектурный конструктор, который можно было бы собирать под разные условия: быстро и экономно строить двух-, трех- или четырехзальные кинотеатры для городов разного масштаба. И у него получилось: своя «Родина» появилась в Москве, Екатеринбурге, Челябинске и других городах.&lt;p&gt;&lt;a href=https://engineer.yadro.com/article/kak-konstruktivist-kornfeld-izmenil-arhitekturu-kinoteatrov/&gt;Узнать больше →&lt;/a&gt;&lt;blockquote&gt;&lt;p&gt;Больше об истории авангарда и его связи с разработками YADRO узнаете на экскурсии «Вычисляя архитектуру». Билеты — &lt;a href=&#34;https://yadro-engineer-history.ru/?utm_source=habr&amp;amp;utm_medium=referral&amp;amp;utm_campaign=announcement_season2_300426&amp;amp;utm_referrer=habr.com&#34;&gt;на лендинге проекта&lt;/a&gt;.&lt;/blockquote&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/yadro/articles/1030070/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030070</guid>
      <pubDate>Thu, 30 Apr 2026 10:42:22 +0000</pubDate>
    </item>
    <item>
      <title>Большое сравнение процессоров Intel разных лет в играх</title>
      <link>https://habr.com/ru/companies/x-com/articles/1030068/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030068</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/bd5/a0c/8c4/bd5a0c8c45dfc33daf814e9227fbc655.png alt=&#34;Спойлер: старые лучше новых&#34; title=&#34;Спойлер: старые лучше новых&#34; width=1200 height=758 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/bd5/a0c/8c4/bd5a0c8c45dfc33daf814e9227fbc655.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/bd5/a0c/8c4/bd5a0c8c45dfc33daf814e9227fbc655.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Спойлер: старые лучше новых&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Если вы сейчас выбираете процессор Intel, думаете, не пора ли обновить старый, или присматриваетесь к чему-нибудь на вторичке, для вас подвезли немного любопытной статистики. У нас с вами есть &lt;strong&gt;конкретные fps по десяти флагманам Intel за десять лет&lt;/strong&gt;, которые я объединил в сводную таблицу и разложил по полочкам, чтобы понять, что еще тянет современные игры, что уже нет, где подвох с деградацией, и какой процессор лучше купить в 2026. Спойлер: &lt;strong&gt;самый новый — не лучший&lt;/strong&gt;. Результаты, конечно, удивили, но менее любопытными от этого не стали. Вот и давайте в них поковыряемся.&lt;h3&gt;Тестовый стенд и методика&lt;/h3&gt;&lt;p&gt;&lt;strong&gt;Тестирование проводилось в 1080p&lt;/strong&gt; — это стандартная методика Hardware Unboxed для процессорных тестов, потому что в более высоких разрешениях упор смещается на видеокарту и разница между CPU размывается. Видеокарта — &lt;a href=&#34;https://www.xcom-shop.ru/palit_geforce_rtx_5090_gamerock_ne75090019r5-gb2020g_1187980.html?utm_source=khabr_soci&amp;amp;utm_medium=social&amp;amp;utm_campaign=sravnenie_protsessorov_Intel%7Cs%7Ckhabr_soci%7Cm%7Csocial%7Cb%7Cna_b%7Cg%7Crussia%7Cp%7Cinhouse%7Cf%7Cna_f%7Ca%7Cna_a&#34;&gt;GeForce RTX 5090&lt;/a&gt;, чтобы она точно не стала узким горлом. Память для платформ LGA 1151/1200 выбрали DDR4, а для LGA 1700/1851 — DDR5. 12900K тестировали с обоими типами.&lt;p&gt;Естественно, если вы играете в 1440p или 4K, разрыв между поколениями будет заметно меньше. &lt;strong&gt;В 1080p мы получаем потолок процессора&lt;/strong&gt;, в 4K — потолок видеокарты. Так что, если сидите на 7700K с хорошей видеокартой — в реальности fps окажется выше, чем дали результаты рассматриваемого тестирования.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/b00/ba2/310/b00ba2310ab2d09b3c80bff538a0ab50.png alt=&#34;Ничего в сборке не становилось узким местом и процессоры при тестировании выдавали свои максимальные результаты.&#34; title=&#34;Ничего в сборке не становилось узким местом и процессоры при тестировании выдавали свои максимальные результаты.&#34; width=1800 height=1148 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/b00/ba2/310/b00ba2310ab2d09b3c80bff538a0ab50.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/b00/ba2/310/b00ba2310ab2d09b3c80bff538a0ab50.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Ничего в сборке не становилось узким местом и процессоры при тестировании выдавали свои максимальные результаты.&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;Рассматривать результаты в каждой конкретной игре для каждого конкретного процессора просто нет смысла. Поэтому я привел именно среднее значение в таблице, ниже и точечно расскажу об отклонениях. В конце концов &lt;strong&gt;мы сравниваем процессоры&lt;/strong&gt;, а не творения разработчиков по части оптимизации. Вот игры, которые прогоняли на каждом процессоре:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Rainbow Six Siege X&lt;li&gt;&lt;p&gt;Counter-Strike 2&lt;li&gt;&lt;p&gt;The Last of Us Part II Remastered&lt;li&gt;&lt;p&gt;ARC Raiders&lt;li&gt;&lt;p&gt;Horizon Zero Dawn Remastered&lt;li&gt;&lt;p&gt;Assetto Corsa Competizione&lt;li&gt;&lt;p&gt;Battlefield 6&lt;li&gt;&lt;p&gt;Borderlands 4&lt;li&gt;&lt;p&gt;Marvel Rivals&lt;li&gt;&lt;p&gt;Baldur&amp;#39;s Gate 3 (DX11)&lt;li&gt;&lt;p&gt;Cyberpunk 2077: Phantom Liberty&lt;li&gt;&lt;p&gt;Space Marine 2&lt;li&gt;&lt;p&gt;Mafia: The Old Country&lt;li&gt;&lt;p&gt;Spider-Man 2&lt;/ul&gt;&lt;h3&gt;Результаты тестирования процессоров Intel в играх&lt;/h3&gt;&lt;div&gt;&lt;div class=table&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Процессор&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Год&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Ядра / потоки&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Средний fps в 14 играх (1080p Ultra)&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;1% Low&lt;/strong&gt;&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i7-7700K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2017&lt;td&gt;&lt;p align=left&gt;4 / 8&lt;td&gt;&lt;p align=left&gt;78&lt;td&gt;&lt;p align=left&gt;54&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i7-8700K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2017&lt;td&gt;&lt;p align=left&gt;6 / 12&lt;td&gt;&lt;p align=left&gt;99&lt;td&gt;&lt;p align=left&gt;71&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-9900K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2018&lt;td&gt;&lt;p align=left&gt;8 / 16&lt;td&gt;&lt;p align=left&gt;113&lt;td&gt;&lt;p align=left&gt;83&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-10900K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2020&lt;td&gt;&lt;p align=left&gt;10 / 20&lt;td&gt;&lt;p align=left&gt;122&lt;td&gt;&lt;p align=left&gt;91&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-11900K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;8 / 16&lt;td&gt;&lt;p align=left&gt;121&lt;td&gt;&lt;p align=left&gt;90&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-12900K (DDR4)&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;16 / 24&lt;td&gt;&lt;p align=left&gt;127&lt;td&gt;&lt;p align=left&gt;94&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-12900K (DDR5)&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2021&lt;td&gt;&lt;p align=left&gt;16 / 24&lt;td&gt;&lt;p align=left&gt;149&lt;td&gt;&lt;p align=left&gt;113&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-13900K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2022&lt;td&gt;&lt;p align=left&gt;24 / 32&lt;td&gt;&lt;p align=left&gt;170&lt;td&gt;&lt;p align=left&gt;127&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core i9-14900K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2023&lt;td&gt;&lt;p align=left&gt;24 / 32&lt;td&gt;&lt;p align=left&gt;172&lt;td&gt;&lt;p align=left&gt;129&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core Ultra 9 285K&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2024&lt;td&gt;&lt;p align=left&gt;24 / 24&lt;td&gt;&lt;p align=left&gt;157&lt;td&gt;&lt;p align=left&gt;117&lt;tr&gt;&lt;td&gt;&lt;p align=left&gt;&lt;strong&gt;Core Ultra 7 270K Plus&lt;/strong&gt;&lt;td&gt;&lt;p align=left&gt;2026&lt;td&gt;&lt;p align=left&gt;24 / 24&lt;td&gt;&lt;p align=left&gt;169&lt;td&gt;&lt;p align=left&gt;130&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;h3&gt;7700K, 8700K, 9900K, 10900K, 11900K — старые процессоры в играх&lt;/h3&gt;&lt;p&gt;Честно говоря, &lt;strong&gt;7700K в 2026 году — это уже впритык&lt;/strong&gt;. 78 fps в среднем — вроде играбельно, но 1% Low в районе 54 fps, а значит микрофризы будут регулярными. В легких играх вроде Counter-Strike 2 или Rainbow Six Siege он еще уверенно держит больше 100 fps. Но стоит включить трассировку лучей в Cyberpunk 2077 или Spider-Man 2 — и fps валится ниже 60. На средних настройках без RT ситуация заметно лучше: средний fps поднимается до 95, а с 1% Low в районе 67. Так что если вы готовы играть без лучей и не на максималках — 7700K все еще скрипит, но едет.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/d23/26c/a67/d2326ca676a883c00fd80286dcb7c2ca.jpeg alt=&#34;Core i7-7700K хоть и старый, но все еще достаточный для не самого требовательного геймера.&#34; title=&#34;Core i7-7700K хоть и старый, но все еще достаточный для не самого требовательного геймера.&#34; width=1200 height=829 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/d23/26c/a67/d2326ca676a883c00fd80286dcb7c2ca.jpeg 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/d23/26c/a67/d2326ca676a883c00fd80286dcb7c2ca.jpeg 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Core i7-7700K хоть и старый, но все еще достаточный для не самого требовательного геймера.&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;8700K — уже совсем другая история&lt;/strong&gt;. Два дополнительных ядра дали ощутимый толчок, особенно в тяжелых играх. В Spider-Man 2 с трассировкой 8700K выдает 53 fps против 39 у 7700K — разница в полтора раза. В Cyberpunk 2077 разрыв еще больше: 76 fps против 51. Правда, в Mafia: The Old Country прирост скромнее — процессор и так нагружен под завязку и дополнительные ядра помогают не так сильно.&lt;p&gt;9900K со своими 113 fps и 10900K &lt;strong&gt;несмотря на почтенный возраст все еще тянут&lt;/strong&gt; даже современные игры. 10900K выдаёт 122 fps в среднем — более чем комфортно для большинства современных игр в 1080p.&lt;p&gt;А вот &lt;strong&gt;11900K — единственный процессор в этом списке, который оказался медленнее своего предшественника&lt;/strong&gt;. Первый раз за десятилетие Intel выпустила новое поколение, которое пошло назад. Если у вас стоит 10900K, а в свое время вы хотели купить новое поколение — вы ничего не потеряли.&lt;h3&gt;12900K, 13900K, 14900K — быстрые процессоры, но с недостатками&lt;/h3&gt;&lt;p&gt;&lt;a href=&#34;https://www.xcom-shop.ru/intel_core_i9-12900k_874298.html?utm_source=khabr_soci&amp;amp;utm_medium=social&amp;amp;utm_campaign=sravnenie_protsessorov_Intel%7Cs%7Ckhabr_soci%7Cm%7Csocial%7Cb%7Cna_b%7Cg%7Crussia%7Cp%7Cinhouse%7Cf%7Cna_f%7Ca%7Cna_a&#34;&gt;Core i9-12900K&lt;/a&gt; стал первым процессором Intel с гибридной архитектурой и поддержкой DDR5. С DDR4 он выдает 127 fps. На DDR5 — уже 149 fps. Разница в 22 кадра между типами памяти у этого процессора ощутимая. Когда 12900K только вышел, DDR5 почти не давала преимущества в играх, но сейчас они научились использовать ее пропускную способность, особенно с трассировкой лучей.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/e82/c93/aef/e82c93aef52d83a7313e468678df4752.png alt=&#34;Intel i9-12900K - был одним из поворотных моментов в истории бренда.&#34; title=&#34;Intel i9-12900K - был одним из поворотных моментов в истории бренда.&#34; width=1200 height=818 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/e82/c93/aef/e82c93aef52d83a7313e468678df4752.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/e82/c93/aef/e82c93aef52d83a7313e468678df4752.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Intel i9-12900K - был одним из поворотных моментов в истории бренда.&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;a href=&#34;https://www.xcom-shop.ru/intel_i9-13900k_986236.html?utm_source=khabr_soci&amp;amp;utm_medium=social&amp;amp;utm_campaign=sravnenie_protsessorov_Intel%7Cs%7Ckhabr_soci%7Cm%7Csocial%7Cb%7Cna_b%7Cg%7Crussia%7Cp%7Cinhouse%7Cf%7Cna_f%7Ca%7Cna_a&#34;&gt;Core i9-13900K&lt;/a&gt; выдает 170 fps в среднем по 14 играм, 14900K — 172 fps. Разница между ними минимальна: 14900K — это по сути тот же 13900K с небольшим заводским разгоном. Вот только покупать сейчас эти два процессора сродни лотерее.&lt;p&gt;Давайте будем честны: у них есть явная проблема с деградацией. Из-за бага в микрокоде чипы &lt;strong&gt;под высокими нагрузками постепенно теряли стабильность&lt;/strong&gt; — минимальное напряжение для работы на штатных частотах росло, пока процессор просто не переставал нормально функционировать.&lt;p&gt;Intel признала проблему, продлила гарантию на два года и выпустила патчи. Но &lt;strong&gt;если вы берете такой камень с рук — гарантия вам не поможет&lt;/strong&gt;, а степень деградации конкретного экземпляра узнаете уже только после покупки.&lt;h3&gt;Какой процессор лучше для игр&lt;/h3&gt;&lt;p&gt;И вот мы добираемся до архитектуры Arrow Lake. &lt;a href=&#34;https://www.xcom-shop.ru/intel_core_ultra_9_285k_1128119.html?utm_source=khabr_soci&amp;amp;utm_medium=social&amp;amp;utm_campaign=sravnenie_protsessorov_Intel%7Cs%7Ckhabr_soci%7Cm%7Csocial%7Cb%7Cna_b%7Cg%7Crussia%7Cp%7Cinhouse%7Cf%7Cna_f%7Ca%7Cna_a&#34;&gt;Core Ultra 9 285K&lt;/a&gt; позиционировался как полная перезагрузка серии, но в играх это обернулось скорее провалом. В итоге 285K с его 157 fps оказался медленнее 13900K (170 fps) и 14900K (172 fps), и всего на 5% быстрее 12900K на DDR5 (149 fps). Intel сделала ставку на энергоэффективность, но заплатила за это частотами и задержками между чиплетами — а в играх это как раз то, что убивает fps.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/090/046/bc0/090046bc00b1081a21dfc7108ba3517a.png alt=&#34;Core Ultra 9 285K - не просто так поменял нейминг. Это действительно новое поколение процессоров.&#34; title=&#34;Core Ultra 9 285K - не просто так поменял нейминг. Это действительно новое поколение процессоров.&#34; width=1200 height=790 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/090/046/bc0/090046bc00b1081a21dfc7108ba3517a.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/090/046/bc0/090046bc00b1081a21dfc7108ba3517a.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Core Ultra 9 285K - не просто так поменял нейминг. Это действительно новое поколение процессоров.&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;В марте 2026-го Intel выкатила &lt;a href=&#34;https://www.xcom-shop.ru/intel_core_ultra_7_265k_1128113.html?utm_source=khabr_soci&amp;amp;utm_medium=social&amp;amp;utm_campaign=sravnenie_protsessorov_Intel%7Cs%7Ckhabr_soci%7Cm%7Csocial%7Cb%7Cna_b%7Cg%7Crussia%7Cp%7Cinhouse%7Cf%7Cna_f%7Ca%7Cna_a&#34;&gt;Core Ultra 7 270K Plus за чуть меньше чем 30 000 рублей&lt;/a&gt;. По спецификациям это почти полный клон 285K за 50 000 рублей, но частота шины между чиплетами поднялась с 2,1 до 3 ГГц.&lt;p&gt;В играх 270K Plus выдает 169 fps — почти на уровне 14900K (172 fps) и получается, что лучшим игровым процессором всей линейки Arrow Lake оказался не флагман, а его более дешевая переупаковка. Лично я в этом вижу тихое признание Intel того, что 285K оказался неудачным.&lt;h3&gt;Какой процессор Intel выбрать в 2026 году&lt;/h3&gt;&lt;p&gt;Если собираете новую систему прямо сейчас и хотите Intel, но так, чтобы можно было поиграть, то Core Ultra 7 270K Plus выглядит самым разумным вариантом. По fps он на уровне 14900K, но без рисков деградации и с заметно меньшим энергопотреблением. &lt;strong&gt;Брать 285K за 55 000 рублей смысла мало&lt;/strong&gt; — это переплата почти вдвое за то, что в названии стоит «Ultra 9» вместо «Ultra 7», при более низкой игровой производительности.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/b7c/2c5/968/b7c2c5968a4ac1e3aab4d31ed5a4f576.png alt=&#34;Мы сейчас не будем сравнивать Intel и AMD. Но Intel, несмотря на хейт, движется в правильном направлении. Просто пока не дошла до цели.&#34; title=&#34;Мы сейчас не будем сравнивать Intel и AMD. Но Intel, несмотря на хейт, движется в правильном направлении. Просто пока не дошла до цели.&#34; width=1200 height=760 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/b7c/2c5/968/b7c2c5968a4ac1e3aab4d31ed5a4f576.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/b7c/2c5/968/b7c2c5968a4ac1e3aab4d31ed5a4f576.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;&lt;em&gt;Мы сейчас не будем сравнивать Intel и AMD. Но Intel, несмотря на хейт, движется в правильном направлении. Просто пока не дошла до цели.&lt;/em&gt;&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;Если сидите на 7700K или 8700K — апгрейд даст ощутимый результат&lt;/strong&gt;, но имейте в виду: при переходе на Arrow Lake придется менять материнку, память (на DDR5), цена которой сравнима с готовым компьютером год назад и, скорее всего, кулер. А в конце 2026 года Intel обещает Nova Lake с новой платформой, после чего Arrow Lake станет тупиковой веткой — апгрейдиться на ней уже будет некуда. Так что если ваш 7700K, особенно в паре с хорошей видеокартой, еще терпит, есть смысл подождать полгода и посмотреть, что реально покажет Nova Lake.&lt;p&gt;&lt;strong&gt;Если у вас 12900K, 13900K или 14900K — менять пока не на что&lt;/strong&gt;. 270K Plus вам не добавит примерно ничего, а 285K и вовсе скорее всего будет медленнее. Лучше сидите и ждите Nova Lake с его обещанными 52 ядрами и Big Last Level Cache до 144 МБ. Заодно посмотрим как выглядит ответ синих на 3D V-Cache от AMD. Если Intel реально завезет все это и оно заработает, у нас будет первый по-настоящему интересный апгрейд со времен 13900K.&lt;p&gt;Хотя давайте будем честны: в случае с Intel «подождите, скоро будет лучше» — это фраза, которую мы слышим уже не первый год. Компания, которая за десятилетие умудрилась выпустить деградирующие процессоры и проиграть собственному трехлетнему флагману, заслуживает осторожного оптимизма. Но никак не слепой веры.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/x-com/articles/1030068/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030068</guid>
      <pubDate>Thu, 30 Apr 2026 10:39:08 +0000</pubDate>
    </item>
    <item>
      <title>Пользовательская инструкция как инструмент адаптации, а не формальность проекта</title>
      <link>https://habr.com/ru/companies/korus_consulting/articles/1030062/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030062</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;div class=persona&gt;&lt;img class=&#34;image persona__image&#34; src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/97c/e40/1f8/97ce401f865117ae643938b375d01969.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/97c/e40/1f8/97ce401f865117ae643938b375d01969.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/97c/e40/1f8/97ce401f865117ae643938b375d01969.png 781w&#34; loading=lazy decode=async&gt;&lt;h5 class=persona__heading&gt;Наталья Белова&lt;/h5&gt;&lt;p class=persona__text&gt;Аналитик ELMA365 из &lt;a href=&#34;https://career.korusconsulting.ru/departments/crm/?utm_source=internal&amp;amp;utm_medium=social&amp;amp;utm_campaign=department&amp;amp;utm_term=habr&amp;amp;utm_content=description_crm_bp&#34;&gt;департамента CRM&amp;amp;BPM&lt;/a&gt; в «КОРУС Консалтинг»&lt;/div&gt;&lt;p&gt;С инструкциями зачастую приходится разбираться в самый неподходящий момент — когда задача срочная, а техника или система неожиданно начинает работать не так, как нужно. Недавно я открыла руководство к новой стиральной машине. Казалось бы, подробный документ с техническими характеристиками, схемами и предупреждениями перед использованием. Но ответ на простой вопрос «как стирать одеяло?» пришлось поискать.&lt;p&gt;В ИТ-проектах ситуация &lt;strong&gt;удивительно похожа&lt;/strong&gt;. &lt;p&gt;На этапе внедрения любой информационной системы почти всегда приходится готовить пользовательские инструкции. Чаще всего мы идем по привычному пути — даем общее представление, описываем возможности и ограничения системы, перечисляем разделы меню, объекты, кнопки, поля. Пошагово, со скриншотами. Получается структурированный и обстоятельный документ внушительного объема. Формально все сделано правильно.&lt;strong&gt; Но если честно — кто это читает? &lt;/strong&gt;&lt;p&gt;Привет, Хабр! Я Наталья Белова, аналитик ELMA365 из &lt;a href=&#34;https://career.korusconsulting.ru/departments/crm/?utm_source=internal&amp;amp;utm_medium=social&amp;amp;utm_campaign=department&amp;amp;utm_term=habr&amp;amp;utm_content=description_crm_bp&#34;&gt;департамента CRM&amp;amp;BPM&lt;/a&gt; в «КОРУС Консалтинг». В этой статье расскажу, как научиться создавать инструкции, которые будут открывать и помогут пользователям легче адаптироваться к изменениям в привычной системе работы.&lt;figure class=full-width&gt;&lt;img src=https://habrastorage.org/r/w1560/getpro/habr/upload_files/cf0/124/6c6/cf01246c699ec56dc3f85c81a249b58d.png width=780 height=440 sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/getpro/habr/upload_files/cf0/124/6c6/cf01246c699ec56dc3f85c81a249b58d.png 780w,&#xA;       https://habrastorage.org/r/w1560/getpro/habr/upload_files/cf0/124/6c6/cf01246c699ec56dc3f85c81a249b58d.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;h2&gt;Почему типовые инструкции не решают проблемы&lt;/h2&gt;&lt;p&gt;Если отбросить формальности, их главные задачи — помочь человеку принять изменения, быстрее освоить новые механизмы, избежать типовых ошибок, снизить зависимость от команды поддержки и аналитиков после запуска.&lt;p&gt;Внедрение новой системы всегда меняет привычный способ работы. У каждого пользователя уже есть своё понимание процессов и «автопилот». Поэтому любые изменения, когда нужно перестроиться, разобраться и научиться чему-то новому, требуют усилий. Если инструкция в этот момент выглядит как техническое описание интерфейса, она вряд ли станет помощником в решении проблем. &lt;p&gt;&lt;strong&gt;Загвоздка в том, что на деле именно так они обычно и выглядят: &lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Пишутся «от системы» и повторяют структуру интерфейса;&lt;li&gt;&lt;p&gt;Не учитывают различия между ролями пользователей;&lt;li&gt;&lt;p&gt;Создаются людьми, которые слишком хорошо знают систему и уже не видят ее глазами новичка;&lt;li&gt;&lt;p&gt;Перегружены деталями; &lt;li&gt;&lt;p&gt;Не фокусируются на реальных сценариях использования;&lt;li&gt;&lt;p&gt;Почти не объясняют, как именно изменится повседневная работа после внедрения.&lt;/ul&gt;&lt;p&gt;В результате &lt;strong&gt;документ существует, но им никто не пользуется.&lt;/strong&gt; Пользователю проще спросить коллегу, написать в проектный чат или сразу обратиться в поддержку, чем открывать многостраничное руководство и искать нужное решение.&lt;p&gt;В какой-то момент в голову пришла мысль, что, возможно, проблема не в пользователях, которые «не читают инструкции», а в нас, которые продолжают писать их не о задачах человека, а о структуре системы. Так стало понятно, что нужно строить руководства от задач пользователя и реальных сценариев его работы. &lt;h2&gt;Наш первый инсайт: делать такие руководства непросто&lt;/h2&gt;&lt;p&gt;Когда мы впервые решили строить инструкции от пользовательских сценариев, это оказалось неожиданно сложно. Казалось бы, что может быть проще, чем изменить структуру документа? Но на практике пришлось пересобрать собственное мышление.&lt;p&gt;Мы привыкли описывать систему: разделы, объекты, кнопки, поля.&lt;p&gt;Теперь нужно было описывать работу человека в системе: его задачи, решения, точки ответственности.&lt;p&gt;&lt;strong&gt;Важный вывод:&lt;/strong&gt; пользователя надо готовить к любым изменениям в привычной логике действий. Этот шаг часто игнорируют и сразу переходят к «нажмите кнопку», — но именно он снимает основное сопротивление и снижает тревожность. &lt;p&gt;Его цель — помочь пользователю через пошаговые действия с объяснениями «зачем», «что изменится», «что останется прежним» психологически и организационно подготовиться к новой логике работы.&lt;p&gt;Поэтому в нашей методологии появился отдельный подготовительный этап. Дальше расскажу о нем подробнее. &lt;h3&gt;Шаг 1. Определите цель внедрения системы&lt;/h3&gt;&lt;p&gt;Она должна быть сформулирована языком бизнеса, а не проектной документации. Пользователю важно понимать, зачем вообще появилась новая система и какую проблему она решает лично для него:&lt;p&gt;❌ Внедрение системы класса X для автоматизации процессов Y.&lt;p&gt;✅ Внедрение новой системы, чтобы сократить время обработки заявок/ сделать процессы прозрачнее/ убрать дублирование данных/ исключить потерю информации.&lt;h3&gt;Шаг 2. Покажите, что изменится в работе&lt;/h3&gt;&lt;p&gt;Сфокусируйтесь не на абстрактных формулировках, а на том, как именно изменится повседневная работа сотрудника.&lt;p&gt;Например:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Появится единое рабочее пространство для всех участников проекта;&lt;li&gt;&lt;p&gt;Статусы задач теперь будут отображаться онлайн;&lt;li&gt;&lt;p&gt;Часть рутинных задач станет автоматизированной;&lt;li&gt;&lt;p&gt;Некоторые поля станут обязательными к заполнению;&lt;li&gt;&lt;p&gt;Необходимо будет фиксировать результаты в системе.&lt;/ul&gt;&lt;p&gt;Затем свяжите эти изменения с практическими сценариями: «После согласования больше не придется отправлять письмо — система сама уведомит следующего участника».&lt;h3&gt;Шаг 3. Обозначьте, что НЕ изменится&lt;/h3&gt;&lt;p&gt;Этот пункт часто недооценивают, а зря. &lt;p&gt;Люди легче принимают изменения, когда понимают, что новая система не обесценивает их опыт, не отменяет роль и не забирает зону ответственности. &lt;p&gt;&lt;strong&gt;Отдельно подчеркните, что сохраняется:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Зона принятия решений остаётся за руководителем;&lt;li&gt;&lt;p&gt;Ответственность за корректность данных остаётся за исполнителем;&lt;li&gt;&lt;p&gt;Регламент согласования не меняется, меняется только инструмент.&lt;/ul&gt;&lt;p&gt;Такие акценты помогут начать воспринимать нововведения как рабочий механизм, который поддерживает &lt;strong&gt;уже существующие процессы.&lt;/strong&gt;&lt;h3&gt;Шаг 4. Объясните, какие проблемы решаются и какой результат ожидается&lt;/h3&gt;&lt;p&gt;Если пользователь видит в инструкции решение реальной проблемы, которая каждый день мешает ему работать, то у него начинает формироваться реальное доверие к инструкции.&lt;p&gt;Говорите на языке «рабочих болей»:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Больше не придется вручную собирать информацию из разных источников;&lt;li&gt;&lt;p&gt;Исчезнут «потерянные» заявки;&lt;li&gt;&lt;p&gt;Уменьшится количество ручных операций;&lt;li&gt;&lt;p&gt;Снизится риск ошибок из-за человеческого фактора; &lt;li&gt;&lt;p&gt;Процесс согласования ускорится в Х раз.&lt;/ul&gt;&lt;h3&gt;Шаг 5. Разделите зоны ответственности системы и человека&lt;/h3&gt;&lt;p&gt;В век повсеместной автоматизации особенно важно проговаривать, что будет делать система, а что по-прежнему останется за пользователем. Раздел помогает выстроить корректные ожидания, снижает количество вопросов после запуска и снимает один из скрытых страхов внедрения: «Теперь все будет делать система, а моя роль обесценится?»&lt;p&gt;Зафиксируйте три блока: &lt;ul&gt;&lt;li&gt;&lt;p&gt;Что система выполняет автоматически: присваивает номера, контролирует сроки, отправляет уведомления;&lt;li&gt;&lt;p&gt;Какие действия остаются за пользователем;&lt;li&gt;&lt;p&gt;Где требуется решение, проверка или экспертная оценка человека.&lt;/ul&gt;&lt;p&gt;Когда понятны цели и роли, можно переходить к практическим сценариям. В них нужно показать конкретные действия пользователя в системе.&lt;h3&gt;Шаг 6. Определите роли пользователей&lt;/h3&gt;&lt;p&gt;Разделение ролей помогает сделать инструкцию точной и не перегружать сотрудника лишней информацией, которая не относится к его зоне ответственности.&lt;p&gt;Это может быть разделение на:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Бухгалтера&lt;li&gt;&lt;p&gt;Менеджера&lt;li&gt;&lt;p&gt;Оператора&lt;li&gt;&lt;p&gt;Руководителя&lt;li&gt;&lt;p&gt;Администратора. &lt;/ul&gt;&lt;p&gt;Для каждой роли необходимо сформировать и описать набор задач и действий в системе, а затем оформить отдельным разделом инструкции. Так пользователю будет проще найти нужную информацию и применить ее в работе.&lt;h3&gt;Шаг 7 . Сформируйте список сценариев&lt;/h3&gt;&lt;p&gt;Пользователь мыслит своими задачами, а не разделами системы, поэтому сценарии нужно формировать, отталкиваясь от его целей. &lt;p&gt;❌Работа со справочником контрагентов&lt;p&gt;✅Создать карточку нового клиента / зарегистрировать заявку / согласовать договор / сформировать отчет&lt;p&gt;Проще всего собрать список сценариев, если разложить бизнес-процесс на шаги и описать, что делает каждая роль.&lt;h3&gt;Шаг 8. Опишите сценарии через задачу и результат&lt;/h3&gt;&lt;p&gt;Пользователю важно понимать не только «куда нажать», но и к какому результату приведут его действия. &lt;p&gt;Попробуйте придерживаться общей структуры описания сценариев:&lt;ol&gt;&lt;li&gt;&lt;p&gt;Какая цель у сценария;&lt;li&gt;&lt;p&gt;В каких случаях он используется;&lt;li&gt;&lt;p&gt;Какие действия пользователю нужно сделать вне системы;&lt;li&gt;&lt;p&gt;Какие действия нужно сделать в системе;&lt;li&gt;&lt;p&gt;Что изменится после выполнения всех шагов.&lt;/ol&gt;&lt;h3&gt;Шаг 9. Добавьте контрольные точки&lt;/h3&gt;&lt;p&gt;Они снижают количество ошибок и помогают самостоятельно проверить себя без обращения за помощью.&lt;p&gt;Что стоит указать:&lt;ol&gt;&lt;li&gt;&lt;p&gt;На что обратить внимание при выполнении шагов;&lt;li&gt;&lt;p&gt;Как проверить, что действия выполнены корректно;&lt;li&gt;&lt;p&gt;Какие статусы или изменения должны появиться в системе.&lt;/ol&gt;&lt;h3&gt;Шаг 10. Опишите типовые ошибки&lt;/h3&gt;&lt;p&gt;Это один из самых востребованных блоков любой инструкции, потому что чаще всего пользователь ищет не «как сделать что-то», а «почему все пошло не так и как это быстро исправить». &lt;p&gt;Что важно включить в раздел:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Какие ошибки возникают чаще всего;&lt;li&gt;&lt;p&gt;К чему они приводят;&lt;li&gt;&lt;p&gt;Как их исправить.&lt;/ul&gt;&lt;h3&gt;Шаг 11. Продумайте и опишите нестандартные ситуации&lt;/h3&gt;&lt;p&gt;Инструкция становится действительно полезной, когда в том числе помогает разобраться с тем, что выходит за рамки стандартного сценария.&lt;p&gt;Например, это может быть описание действий в случае, если возникла проблема, потому что: &lt;ul&gt;&lt;li&gt;&lt;p&gt;Ответственный исполнитель отсутствует;&lt;li&gt;&lt;p&gt;Документ нужно изменить после отправки;&lt;li&gt;&lt;p&gt;Данные внесены с ошибкой;&lt;li&gt;&lt;p&gt;Процесс остановился на согласовании.&lt;/ul&gt;&lt;h3&gt;Шаг 12. Объясните, как фиксировать проблемы грамотно&lt;/h3&gt;&lt;p&gt;Даже при наличии подробной инструкции часть вопросов всё равно потребует обращения за помощью. Важно сделать этот процесс понятным и быстрым, чтобы снизить нагрузку на поддержку и сделать взаимодействие с ИТ-командой эффективнее. &lt;p&gt;В алгоритме можно описать следующие шаги:&lt;ol&gt;&lt;li&gt;&lt;p&gt;С чего начать поиск причины;&lt;li&gt;&lt;p&gt;Какую информацию подготовить для технической поддержки;&lt;li&gt;&lt;p&gt;Куда и в каком формате обращаться за помощью.&lt;/ol&gt;&lt;h2&gt;Чек-лист: как понять, что инструкция получилась рабочей?&lt;/h2&gt;&lt;p&gt;Проверьте ее по критериям:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Есть вводная часть, описывающая цели изменений или внедрения новой системы.&lt;li&gt;&lt;p&gt;Пользователь может быстро найти нужный сценарий.&lt;li&gt;&lt;p&gt;Сценарии исходят от целей, задач, ролей пользователей и включают ожидаемые результаты.&lt;li&gt;&lt;p&gt;Есть раздел по решению типовых ошибок.&lt;li&gt;&lt;p&gt;Есть раздел с описанием действий в нестандартных ситуациях.&lt;li&gt;&lt;p&gt;Инструкция отвечает на вопрос «что делать?», а не «как устроено?».&lt;/ul&gt;&lt;p&gt;Такой подход требует больше усилий на старте по сравнению с классическим «опишем меню и кнопки», при этом не исключает и описание интерфейсной части: настройки профиля, поиск, фильтры, стандартные возможности системы. Он дополняет его и делает осмысленнее и удобнее для пользователя.&lt;p&gt;В итоге вы получаете реальный инструмент адаптации, который помогает сотрудникам перестроить повседневную работу. &lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <guid>https://habr.com/ru/companies/korus_consulting/articles/1030062/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030062</guid>
      <pubDate>Thu, 30 Apr 2026 10:33:09 +0000</pubDate>
    </item>
    <item>
      <title>Владелец наркобизнеса из даркнета о РКН и импортозамещении</title>
      <link>https://habr.com/ru/articles/1030060/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030060</link>
      <description>&lt;div&gt;&lt;div class=&#34;article-formatted-body article-formatted-body article-formatted-body_version-2&#34;&gt;&lt;div xmlns=http://www.w3.org/1999/xhtml&gt;&lt;p&gt;Владелец наркобизнеса из даркнета о РКН и импортозамещении.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/be/90/95/be9095106aa7b5079bf92d2eb427d28d.jpg sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/be/90/95/be9095106aa7b5079bf92d2eb427d28d.jpg 780w,&#xA;       https://habrastorage.org/r/w1560/webt/be/90/95/be9095106aa7b5079bf92d2eb427d28d.jpg 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;&lt;em&gt;— Иностранные агенты и враги сидят внутри системы и прямо сейчас душат вас блокировками. Они относятся к вам как кривозубым крестьянам из XIX века. Словно вы неразумные дети, которым нельзя давать в руки острые предметы или разрешать смотреть в окно, потому что там злой серый волк перепишет вам код в голове. Это и есть высшая форма русофобии. Тотальное неверие, в интеллект, волю и критическое мышление русского человека.&lt;/em&gt;&lt;hr&gt;&lt;p&gt;В этой статье мы разберём первую часть популярного видео с точки зрения маркетинга, манипулятивных приёмов и политтехнологий. Вполне возможно, что автор вовсе не выражает своё мнение — как могло показаться на первый взгляд, — и оно далеко не так однозначно, как вы решили. Возможно, видео формирует совсем не ту картину мира, которая складывается после просмотра, и преследует совершенно иную цель. Ну и главное — мы дожмём и ответим на вопросы, которые были озвучены, но так и не раскрыты в видео.&lt;p&gt;Как автору удаётся одновременно говорить с двумя лагерями, которые категорически не согласны друг с другом, — и при этом заставлять каждый из них кивать в знак согласия? Какие приёмы позволяют вложить в одну и ту же фразу два противоположных послания — по одному для каждой стороны? И главное — делает ли он это осознанно?&lt;hr&gt;&lt;p&gt;Для начала стоит оговориться: все тезисы вполне рациональны и опираются на ценности, которые сложно оспорить. Именно поэтому интересно исследовать, как профессионалы встраивают собственный пиар в хайповые темы, используя общепринятые идеи в качестве фундамента. И да, те чиновники, что придут на смену старым, вооружатся ровно такими же методами. Поэтому очень полезно понимать и уметь распознавать все эти хитрости.&lt;p&gt;На Ютубе есть такой канал — &lt;code&gt;М О Р И А Р Т И&lt;/code&gt;. В двух словах, это персонаж в маске, позиционирующий себя как владелец дарквебовского маркетплейса, который торгует роскомнадзорными медикаментами. Заметно, что на канал не жалеют бюджета: эффектный монтаж, красивые кадры, качественный звук и, главное, — очень мощные манипулятивные тексты. Я смотрю его видео как учебники по маркетингу.&lt;p&gt;Думаю, этот канал вовсе не «авторский блог», а очень эффективный, хорошо финансированный отдел маркетинга, который воплощает свои самые смелые идеи. Уверен, за этим действительно стоит могущественный и нелегальный бизнес. Однажды я общался с очень талантливой девушкой из сферы маркетинга, она работала с заказчиками из этой ниши, они действительно очень щедро платят, аккумулируя вокруг себя талантливых маркетологов, и некоторые их рекламные кампании действительно впечатляют. Я давно хотел взять на обзор какое-нибудь видео этого Мориарти и разложить его с точки зрения маркетинга, науки или технической стороны.&lt;p&gt;Но то, что вышло 28 апреля, просто невозможно оставить без внимания. Видео под заголовком: &lt;strong&gt;&lt;em&gt;Разоблачение коррупционеров — Кто блокирует интернет в России.&lt;/em&gt;&lt;/strong&gt;&lt;p&gt;В видео много политического контента, но Хабр этого не одобряет. Я не хочу подставлять Хабр, поэтому постараюсь смягчить все провокационные формулировки. Хотя лично моё мнение — обсуждать происходящее, не обсуждая политику, — это как пытаться объяснить, почему в комнате слон, описывая только мебель вокруг него. Или объяснять, почему потонул Титаник, не упоминая айсберг. Проводить код-ревью, не имея доступа к исходному коду. Читать stack trace, полностью игнорируя первые строки. Дебажить прод, не имея доступа к логам…&lt;p&gt;&lt;strong&gt;Но всё равно, я уважаю правила Хабра, поэтому просьба — не обсуждать политику в комментариях, а если и пишете что-то связанное с ней, то с упором на техническую часть и без перехода на личности. Избегайте токсичного формата коммуникаций, даже в завуалированной форме.&lt;/strong&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;Видео начинается очень сильно — с цепляющих и действительно откликающихся тезисов.&lt;/strong&gt;&lt;p&gt;&lt;em&gt;— Знаете, зачем на самом деле заблокировали YouTube, Instagram и зачем сейчас блокирует Telegram? Кто конкретно и почему это делает? Ну и, конечно, к чему это приведёт?&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Ответ на вопрос, кто заблокировал всё перечисленное, очень прост, и вы прекрасно его знаете — государство, которое желает контролировать всё информационное пространство. А если быть точнее — субъекты/субъект, находящийся в его руководстве. Но ответить прямо на данном этапе — значит спугнуть всю интригу, а вместе с ней и огромную часть аудитории, которая не терпит, чтобы кто-то ругал власть. Намного полезнее добавить загадочности: человек сам додумает то, что хочет услышать.&lt;/strong&gt;&lt;p&gt;&lt;em&gt;— Интересный факт про месседжер Макс. Компания, которая владеет и занимается разработкой Макс - это мутная связка кипрских офшоров.&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Спойлер — дальше никакого расследования, что за офшоры и как они устроены, не будет. Это просто универсальный факт, который зритель нужным образом дополнит самостоятельно.&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Те, кто поддерживает власть, скажут — так и знал, всё это вражеский Запад разворовывает нашу страну.&lt;/strong&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Оппозиционная часть скажет — ну естественно, коррумпированные чиновники всё воруют и увозят за рубеж.&lt;/strong&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Позже мы разберём эту тему отдельно и заполним пробелы, которые оставил сценарист видео.&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Характеристика данного фрагмента — Никакой конкретики и, главное, никаких противоречий: две совершенно не согласные друг с другом аудитории только что полностью согласились с автором.&lt;/strong&gt;&lt;p&gt;&lt;em&gt;— WhatsApp, Instagram, YouTube и Telegram. Что объединяет все эти сервисы? Возможно, они все созданные на западе и используются с целью информационной войны против нашего государства или их объединяет независимость и свобода? (Ироничный смех автора)&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Два противоречивых тезиса объединены в один с целью иронично посмеяться. Над чем конкретно смех — непонятно: то ли над «информационной войной против нашего государства», то ли над «независимостью и свободой». Тот же приём:&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Патриоты додумают, что смех над «лживой западной независимостью и свободой».&lt;/strong&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Либералы подумают, что смех над «страшной внешней угрозой».&lt;/strong&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;— Все это лозунги используются для оправдания блокировок. Телеграм же использует их для своего маркетинга. Но где же правда? Все эти платформы являются очень качественными и сильными продуктами, лучшими в своём роде. Ими пользуется каждый и делает это добровольно, потому что они дополняют жизнь, делают её более качественной.&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Ну, тут мало кто будет спорить.&lt;/strong&gt;&lt;p&gt;&lt;em&gt;— Главный аргумент блокировок - защита от иностранной пропаганды. Вас пытаются убедить, что российский зритель настолько глуп и беспомощен, что любой ролик в интернете может перепрошить его сознание. Вас держат за лабораторных крыс, которым нужно фильтровать воздух, чтобы они не вдохнули неправильные идеи.&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Тут начинается эмоциональное раскачивание и той и другой части аудитории. Если вы сторонник идеи, что в спину дышит вражеская пропаганда, то автор заигрывает с вашим эго — разве вы сами, что ли, не разберётесь без РКН, где пропаганда, а где нет? Ну а если вы вообще считаете, что никакой злобной вражеской пропаганды не существует, то тем более эмоционально раскачаетесь — вас за дурака держат, навязывая блокировки от несуществующей проблемы.&lt;/strong&gt;&lt;hr&gt;&lt;p&gt;&lt;strong&gt;На несколько ближайших предложений, видео будто поворачивается к другой части своей аудитории — представителям власти. Тут все универсально.&lt;/strong&gt;&lt;p&gt;&lt;em&gt;— Ну давайте включим логику. Если ваша идеология сильна, если ваши смыслы правдивы, зачем вам заборы?&lt;/em&gt;&lt;p&gt;&lt;em&gt;— Вспомните холодную войну. Почему советские люди охотились за джинсами и пластинками Beatles? Потому что запретный плод сладок, а свой продукт был серым и скучным. Блокируя YouTube, государство расписывается в собственном бессилии. Оно признаёт, мы не можем сделать контент интереснее, чем у них. Мы не можем победить в честном споре. Поэтому мы просто заткнём им рот, а вам завяжем глаза.&lt;/em&gt;&lt;p&gt;&lt;em&gt;— Настоящая победа - это создание своей пропаганды, но такой качественной, чтобы весь мир включал VPN, лишь бы посмотреть наше шоу. Но строить - это сложно, разрушать и запрещать гораздо дешевле.&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Обезличенная критика то ли власти, то ли ещё кого-то. Но весьма аргументированно.&lt;/strong&gt;&lt;hr&gt;&lt;p&gt;&lt;em&gt;— Ответ, как всегда, кроется в цепочке прибыли. Когда вы создаёте продукт, который любят сотни миллионов, как Telegram или YouTube, вы богатеете за счёт инноваций и рекламы. Но когда вы строите, ВК видео или чудо месседжер Макс, вы богатеете за счёт бюджета. Чувствуете разницу?&lt;/em&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;В первом случае судья — рынок и пользователь. Если продукт говно, им не пользуются.&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Во втором случае судья — чиновник подписывающий акт приемки.&lt;/em&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;— И чем хуже работает импортозамещённый аналог, тем больше миллиардов можно попросить на его доработку. Это бесконечный цикл освоения ваших налогов.&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Просто база и еще немного раскачки на эмоции затрагивая тему налогов. Двигаемся дальше.&lt;/strong&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Блокировка Ютуба связана конкретно с неуспешностью ВК/рутуба.&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Блокировка Telegram с провальным релизом мессенджера Макс.&lt;/em&gt;&lt;/ul&gt;&lt;p&gt;&lt;em&gt;— Заметили, как по времени совпадают эти события? Имея миллиардные бюджеты и фактическую монополию на рынке, которые обеспечивают блокировки, ВК продолжает накручивать просмотры своим избранным блогерам, чтобы в статистике всё выглядело так, словно ВКонтакте достойный конкурент Ютубу.&lt;/em&gt;&lt;p&gt;&lt;em&gt;— В России сейчас смешанная экономика с преобладанием государственного капитализма. Это не свободный рынок в классическом либертарианском понимании и неплановая экономика советского типа. Это система, где извлечение прибыли является главной целью. Но правила игры на поле определяются и жёстко контролируются государством.&lt;/em&gt;&lt;p&gt;&lt;em&gt;— Так вот, Макс, Рутуб и ВК хоть и являются государственными проектами, а значит, обязаны быть самыми сильными и лучшими представителями рынка при российских правилах игры таковыми не являются. А главное, они не приносят прибыль, но жрут миллиарды государственных бюджетов. Миллиарды рублей? Нет, долларов!&lt;/em&gt;&lt;p&gt;&lt;em&gt;— И что вы думаете? В таком случае никто не спрашивает, куда делись деньги и где результаты. Представьте себе чиновника из Роскомнадзора или топ-менеджера государственного IT-гиганта. Перед ним стоит задача за 3 года и 5 млрд бюджетных денег создать убийцу YouTube. Деньги потрачены, сроки вышли. А убийца едва дышит, путаясь в собственных гениальных рекламных кампаниях и накрученных просмотрах. Что делать? Признать провал?&lt;/em&gt;&lt;p&gt;&lt;em&gt;— В системе это означает не просто увольнение, а политическую смерть, а иногда и вполне реальные сроки. И тогда в ход идёт стратегия выживания паразита. Они начинают врать. Врут верховному правительству. Сдают отчёты, как количество пользователей непрерывно растёт. Но остаются ли эти пользователи? Готовы ли они совершать покупки или продавать рекламу и становиться популярными за счёт их сервисов? В их графиках кривая популярности rutube уходит в стратосферу, а месседжер Макс якобы вот-вот обгонит Telegram по количеству активных пользователей. Но реальность штука упрямая. Люди всё равно сидят в телеге и смотрят YouTube через костыли. И вот здесь коррупционеры находят гениальный, по их мнению, выход. Чтобы их обман не раскрыли, чтобы никто не смог сравнить их мертворожденный продукт с живым рынком, рынок нужно уничтожить.&lt;/em&gt;&lt;p&gt;&lt;strong&gt;Конечно, не критикуется архитектура и её архитектор — критикуются только негодяи, которые мешают построить могущественную страну. Царь хороший, бояре плохие.&lt;/strong&gt;&lt;p&gt;&lt;em&gt;— Те, кто поддерживает и лоббирует блокировки - это пятая колонна. Вредители и настоящие иностранные агенты прямо внутри системы. Они прикрывают свои действия безопасностью, но те, кто реально отвечает в стране за безопасность, недоумевают и страдают от блокировок.&lt;/em&gt;&lt;p&gt;&lt;em&gt;— Как именно работают эти диверсанты и в чём их задачи? Ослепить и ослабить армию и спецслужбы, заблокировать Telegram, который стал основным средством оперативной связи, координации и передачи данных в реальных боевых условиях. Обескровить экономику, лишить малый бизнес площадок для продажи, а IT-сектор - инструментов для развития. Спровоцировать утечку мозгов. Создать невыносимые условия для жизни и работы самых умных и перспективных граждан, вынуждая их увозить свои знания за границу.&lt;/em&gt;&lt;hr&gt;&lt;p&gt;Дальше загадочный автор в маске утверждает, что РКН — это внутренние диверсанты, цель которых всё разрушить — экономику, интернет и, похоже, всё государство.&lt;p&gt;Многое из этих невероятно талантливых с точки зрения маркетинга тезисов очень сильно откликается. Но, к сожалению, никаких откровений в видео нет.&lt;p&gt;Некоторые вещи цитировать уже нет смысла, потому что они вызывают приступ веселья, например:&lt;p&gt;&lt;em&gt;— Настоящие профессионалы в погонах сегодня вынуждены тратить время на настройку VPN, вместо того, чтобы выполнять свои прямые обязанности. Это ли не вредительство? &amp;lt;далее реклама VPN&amp;gt;&lt;/em&gt;&lt;p&gt;Начало у видео было сильным. Но в какой-то момент уже трудно сложить всё это в единую картину мира, в которой все причинно-следственные связи упорядочиваются в логически верное уравнение.&lt;p&gt;Видео транслирует следующую концепцию: Макс, Рутуб и прочие альтернативы зарубежным сервисам — это попытка создать конкурентный продукт, но коррупция и внутренние диверсанты в лице РКН препятствуют этому. По моему скромному мнению, это не совсем так. РКН и «импортозаместительные холдинги» — это две структуры для достижения одной цели — создать изолированное и подконтрольное информационное пространство.&lt;p&gt;Хронология этой истории может быть описана довольно просто: попытка контролировать зарубежные сервисы и интернет не увенчалась успехом, а если не получается хозяйничать в чужом доме, то придётся или его отобрать, или построить свой. Более детально мотивация контроля над интернетом описана в статье: &lt;a href=https://habr.com/ru/articles/1021070/ rel=&#34;noopener noreferrer nofollow&#34;&gt;Почему протоколы шифрования не спасут интернет: корень проблемы глубже, чем ТСПУ&lt;/a&gt;.&lt;p&gt;Кто не хочет отвлекаться от текущей, кратко основные мысли из статьи:&lt;ul&gt;&lt;li&gt;&lt;p&gt;Россия выделит 70 миллиардов рублей на блокировку интернета в течение ближайших трёх лет&lt;li&gt;&lt;p&gt;РКН потратит 2,3 млрд рублей на ИИ для фильтрации интернета и блокировки VPN&lt;li&gt;&lt;p&gt;В 2023 году кассовое исполнение расходов РКН составило 31,1 млрд&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;Власть будет блокировать интернет любой ценой.&lt;/strong&gt; Обвал и обрушение экономики связанные с этим не так страшны, как распространение правды, которая разоблачит то самое враньё, о котором говорил Мориарти. И, к сожалению, протоколы шифрования не помогут. Власть нивелирует протестное настроение, предлагая подконтрольные альтернативы, — и они уже готовы. Не в том качестве, в каком хотелось бы, но готовы, и всё готово к тому, чтобы дёрнуть рубильник — и он будет дёрнут.&lt;p&gt;Сегодня упор на техническую сторону блокировок, завтра — на юридическую. Написали статью об обходе? Пройдёмте с нами. Подключились к VPN несогласованно — пройдёмте с нами. Откуда узнали? Яндекс-браузер, Ozon, Wildberries, приложения банков — уже сейчас собирают реестр пользователей VPN и изучают, с какой стороны подступиться.&lt;p&gt;&lt;strong&gt;Про википедию я рассказывал в статье, которую только что упомянул выше.&lt;/strong&gt;&lt;p&gt;&lt;strong&gt;Судьба Youtube всем известна:&lt;/strong&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/2a/64/4b/2a644b50eaf3c774232d089b6d820fcf.png alt=&#34;Карта доступности Youtube из Википедии&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/2a/64/4b/2a644b50eaf3c774232d089b6d820fcf.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/2a/64/4b/2a644b50eaf3c774232d089b6d820fcf.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Карта доступности Youtube из Википедии&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;p&gt;&lt;strong&gt;А вот относительно ВК, стоит вспомнить как это было.&lt;/strong&gt;&lt;p&gt;Официальным днём основания считается 10 октября 2006 года. В конце ноября была открыта свободная регистрация. Также запущена рекламная кампания по привлечению новых пользователей. По итогам 2007 года среди всех сайтов России «ВКонтакте» лидировал по объёмам трафика. По популярности сайт занимал 2-е место в Рунете, а также лидирующие позиции в СНГ (4-е место в Казахстане, 7-е в Беларуси, 10-е — на Украине). В 2010-м количество зарегистрированных пользователей превысило 100 млн.&lt;p&gt;Позже в соцсети начали появляться аккаунты знаменитостей со всего мира. Так, в 2013-м, 21 марта, голливудский киноактёр Том Круз зарегистрировался в социальной сети и оповестил об этом подписчиков своего блога в другой зарубежной социальной сети. Среди прочих знаменитостей во «ВКонтакте» могли встретиться: Виктория Бекхэм, Джаред Лето, Шакира, Кевин Спейси, Энрике Иглесиас, Софи Эллис-Бекстор, Пол Ван Дайк, Эрос Рамазотти и другие. Прозвучало, как вступительные титры к остросюжетному блокбастеру, но в общем-то оно так и есть.&lt;p&gt;Далее началось то, к чему мы все привыкли сегодня, но мало кто это замечал тогда. В 2013-м, 24 мая, Роскомнадзор внёс домен vk.com и его IP-адрес в &lt;code&gt;единый реестр запрещённых сайтов&lt;/code&gt;, однако уже через несколько часов удалил его оттуда, объяснив свои действия «человеческим фактором». Видимо, дали по рукам свыше за самодеятельность, план-то был намного более коварным.&lt;p&gt;18.02.2014 на сайте vedomosti.ru была опубликована новость под заголовком: «Во “ВКонтакте” начали проверку экономической деятельности соцсети».&lt;p&gt;Отдельные фрагменты из текста новости для быстрого понимания сути:&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Цель проверки — понять, были ли нарушения со стороны менеджмента при принятии тех или иных решений и заключении сделок.&lt;/em&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;В частности, у фонда есть претензии к тому, что гендиректор «В контакте» Павел Дуров развивает свой личный проект — мессенджер Telegram — на деньги компании.&lt;/em&gt;&lt;li&gt;&lt;p&gt;Представитель UCP прокомментировал ситуацию так: &lt;em&gt;У нас целый список претензий к Дурову по событиям последних двух лет. Мы предложили обычную процедуру в данной ситуации — провести внутреннее расследование под контролем совета директоров компании с возможностью всем членам совета участвовать в формировании задания аудиторам. По непонятным нам причинам такая прозрачная процедура до сих пор не согласована. Вместо этого менеджмент пытается контролировать процесс, т. е. проверять самих себя, что очевидно абсурдно&lt;/em&gt;&lt;li&gt;&lt;p&gt;Сам Дуров в комментариях к новости о претензиях &lt;a href=https://ru.wikipedia.org/wiki/United_Capital_Partners rel=&#34;noopener noreferrer nofollow&#34;&gt;UCP&lt;/a&gt; высказался следующим образом: &lt;em&gt;Их подлая борьба за контроль над компанией бессмысленна; в конечном итоге все равно победит честность, добро и справедливость.&lt;/em&gt;&lt;/ul&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/64/df/6f/64df6f73529bdd6b770acf9e8b5df1ec.png alt=&#34;Сама страница с новостью удалена с ресурса&#34; sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/64/df/6f/64df6f73529bdd6b770acf9e8b5df1ec.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/64/df/6f/64df6f73529bdd6b770acf9e8b5df1ec.png 781w&#34; loading=lazy decode=async&gt;&lt;div&gt;&lt;figcaption&gt;Сама страница с новостью удалена с ресурса&lt;/figcaption&gt;&lt;/div&gt;&lt;/figure&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/2b/61/84/2b618493716726aced4533b4ef4acd9b.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/2b/61/84/2b618493716726aced4533b4ef4acd9b.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/2b/61/84/2b618493716726aced4533b4ef4acd9b.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;details class=spoiler&gt;&lt;summary&gt;Ссылки&lt;/summary&gt;&lt;div class=spoiler__content&gt;&lt;p&gt;&lt;a href=https://www.vedomosti.ru/tech/news/22964621/vo-v-kontakte-nachali-proverku-ekonomicheskoj-deyatelnosti rel=&#34;noopener noreferrer nofollow&#34;&gt;vedomosti.ru: Сама страница с новостью удалена с ресурса&lt;/a&gt;&lt;p&gt;&lt;a href=https://web.archive.org/web/20140218210214/http://www.vedomosti.ru/tech/news/22964621/vo-v-kontakte-nachali-proverku-ekonomicheskoj-deyatelnosti rel=&#34;noopener noreferrer nofollow&#34;&gt;webarchive: Архивная ссылка на новость&lt;/a&gt;&lt;/div&gt;&lt;/details&gt;&lt;p&gt;В 2014 году Павел Дуров публикует &lt;a href=&#34;https://vk.com/durov?w=wall1_45617%2F4dd4243f1bcee368c2&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;пост&lt;/a&gt; на своей официальной странице ВКонтакте. В нём он называет свою должность генерального директора &lt;strong&gt;&lt;em&gt;формальной&lt;/em&gt;&lt;/strong&gt; в результате последних изменений акционерного состава ВКонтакте.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/67/b4/ea/67b4ea278d7cb6f9b4344e0d1afc3161.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/67/b4/ea/67b4ea278d7cb6f9b4344e0d1afc3161.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/67/b4/ea/67b4ea278d7cb6f9b4344e0d1afc3161.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;2014 год отличился еще одним событием. &lt;a href=&#34;https://www.rbc.ru/politics/16/04/2014/57041b4a9a794761c0ce9040?utm_referrer=https%3A%2F%2Fwww.google.com%2F&#34; rel=&#34;noopener noreferrer nofollow&#34;&gt;РБК публикует новость&lt;/a&gt;.&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/be/35/8c/be358c93c5660485af1d96d2a3c35940.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/be/35/8c/be358c93c5660485af1d96d2a3c35940.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/be/35/8c/be358c93c5660485af1d96d2a3c35940.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;&lt;em&gt;— По словам Дурова, в процессе ему пришлось пожертвовать многим, в том числе и своей долей “ВКонтакте”. О каком именно процессе идет речь, Дуров не уточнил. При этом основатель сети подчеркнул, что не жалеет о своем выборе. “Защита личных данных людей стоит этого и намного большего. С декабря 2013г. у меня нет собственности, но у меня осталось нечто более важное – чистая совесть и идеалы, которые я готов защищать”, - отметил Дуров.&lt;/em&gt;&lt;p&gt;Думаю, в целом сложилось понимание. Всё неподконтрольное забрали силой, что не удалось отобрать — заблокировали, предложив костыльную «альтернативу».&lt;hr&gt;&lt;p&gt;Ладно, мы немного отвлеклись, у нас обзор видео Мориарти. Видео выстраивает интересную картину мира, затрагивая очень эмоциональные для нас темы. Сценаристы мастерски работают с чувствами своей аудитории и бьют в самое яблочко. Это гениальный маркетинг. Но в отрыве от таинственного голоса человека в маске, этот текст пестрит манипуляциями на хайповую тему и подаёт крайне поверхностную концепцию, не углубляясь в конкретику и исторические примеры. Цель видео достигнута на все 100% — больше одного миллиона просмотров за сутки. Оба лагеря аудитории удовлетворены и аплодируют, продажи и установки VPN растут. А что за VPN? Не товарища ли майора?&lt;p&gt;Попытаюсь описать структуру на которой построен маркетинговый каркас.&lt;ul&gt;&lt;li&gt;&lt;p&gt;Боль — у нас отбирают свободу&lt;li&gt;&lt;p&gt;Враги — коррупционеры и диверсанты внутри&lt;li&gt;&lt;p&gt;Эмоциональное раскачивание — вы для них “скот”, “кривозубые крестьяне”&lt;li&gt;&lt;p&gt;Решение — VPN автора и ссылка на роскомандзорную фармацевтику&lt;li&gt;&lt;p&gt;Призывы к действию - установите VPN, подпишитесь на канал, загляните в ссылки из описания&lt;/ul&gt;&lt;figure&gt;&lt;img src=https://habrastorage.org/r/w1560/webt/4f/c2/02/4fc202f5c64e604d9a30cbb2ef3ece89.png sizes=&#34;(max-width: 780px) 100vw, 50vw&#34; srcset=&#34;https://habrastorage.org/r/w780/webt/4f/c2/02/4fc202f5c64e604d9a30cbb2ef3ece89.png 780w,&#xA;       https://habrastorage.org/r/w1560/webt/4f/c2/02/4fc202f5c64e604d9a30cbb2ef3ece89.png 781w&#34; loading=lazy decode=async&gt;&lt;/figure&gt;&lt;p&gt;Теперь обратим внимание на паттерн «свой среди чужих» — автор себя не причисляет к оппозиции или к власти, он как бы свой для всех. Руководство не критикуем, его просто обманули внутренние диверсанты и коррупционеры, критикуем только среднее звено — РКН и кого-то ещё.&lt;p&gt;Важнейшая часть — заигрывание со встроенным в нас на биологическом уровне &lt;a href=https://ru.wikipedia.org/wiki/%D0%AD%D1%82%D0%BD%D0%BE%D1%86%D0%B5%D0%BD%D1%82%D1%80%D0%B8%D0%B7%D0%BC rel=&#34;noopener noreferrer nofollow&#34;&gt;этноцентризмом&lt;/a&gt;. Патриотическая риторика: мы нация, давшая миру технологии, мы уникальны и неповторимы. Создаёт установку, что автор и зритель — одно целое.&lt;p&gt;Основная задача — быть приемлемым для максимально широкой аудитории. Патриотам он говорит «я за Россию», либерально настроенным — «я за свободу», а обезличенных служителей органов или аппарата власти выносит за скобки: &lt;em&gt;Настоящие профессионалы в погонах сегодня вынуждены тратить время на настройку VPN.&lt;/em&gt;&lt;p&gt;Точно по такой же методологии работают политтехнологи в предвыборных кампаниях и по таким же методичкам пишутся все новости. Не стоит думать, что я подвергаю критике все тезисы, представленные авторами видео; нет, среди сказанного очень много полезной информации, которая способна конструктивно повлиять на формирование благородного мировоззрения. Главное — быть бдительным.&lt;hr&gt;&lt;p&gt;И на последок, хочу высказаться на тему цензуры. Цензура — это система запретов и ограничений. Она нужна: без неё не будет никаких рамок дозволенного, и интернет быстро превратится в свалку мусора. Но, по-моему, РКН совершенно не справляется с этой задачей. Неужели нам нужно выделять на это 100 млрд рублей? Хабр берёт своё начало в 2006-м, РКН — в 2008-м. И Хабр на протяжении всех этих лет не просто прекрасно модерирует свой контент, но и генерирует прибыль. Здесь вы не встретите ни порнографии, ни нарушений закона, ни призывов к неподобающему поведению.&lt;p&gt;В РКН работает около 3 000 человек, которые, к слову, &lt;a href=https://habr.com/ru/articles/357228/ rel=&#34;noopener noreferrer nofollow&#34;&gt;в 2016 году умудрились заблокировать сами себе localhost&lt;/a&gt;, а на Хабре — около 2 миллионов пользователей. Квалифицированных айтишников, которые отлично понимают, как устроены протоколы шифрования, и знают, как противостоять техническим угрозам. Так, может быть, доверить полномочия РКН Хабру? Заодно сэкономим 100 млрд. А представляете, сколько достойных альтернатив зарубежным сервисам можно было бы создать на эти деньги в условиях честной конкуренции, если бы их осваивали мы, а не три тысячи человек из РКН?&lt;p&gt;А это я ещё не причисляю сюда википедистов и представителей других самоорганизованных сообществ, которые годами поддерживают качество контента на чистом энтузиазме — без единого рубля из бюджета. Если объединить экспертизу всех этих людей, мы получим армию специалистов, которая на голову превосходит РКН и по компетенциям, и по мотивации. И всё это — бесплатно.&lt;hr&gt;&lt;p&gt;Я веду свой &lt;a href=https://t.me/gtosss_group rel=&#34;noopener noreferrer nofollow&#34;&gt;Telegram-канал&lt;/a&gt;, в котором активно публикую волнующие меня новости, свои мысли и технические статьи для IT-специалистов.&lt;p&gt;&lt;strong&gt;Напоминаю, что обсуждение политики является нарушением правил Хабра, уважайте друг друга в комментариях. Я не хочу, чтобы кто-то слил карму из-за политических дебатов на техническом ресурсе. Мы здесь, чтобы обсуждать то, как работает IT с технической точки зрения.&lt;/strong&gt;&lt;hr&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</description>
      <author>gtosss</author>
      <guid>https://habr.com/ru/articles/1030060/?utm_source=habrahabr&amp;utm_medium=rss&amp;utm_campaign=1030060</guid>
      <pubDate>Thu, 30 Apr 2026 10:32:16 +0000</pubDate>
    </item>
  </channel>
</rss>