<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <language>ru</language>
    <title>Рефакторинг</title>
    <description/>
    <link>https://tproger.ru/tag/refactoring</link>
    <atom:link href="https://tproger.ru/tag/refactoring/feed" rel="self" type="application/rss+xml"/>
    <lastBuildDate>Sun, 04 Oct 2026 03:45:59 GMT</lastBuildDate>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>12</sy:updateFrequency>
    <image>
      <title>Рефакторинг</title>
      <link>https://tproger.ru</link>
      <url>https://tproger.ru/apple-touch-icon.png</url>
    </image>
    <item>
      <title>Почему команда растёт, а фичи выходят медленнее</title>
      <link>https://tproger.ru/articles/pochemu-komanda-rastyot-a-fichi-vyhodyat-medlennee</link>
      <comments>https://tproger.ru/articles/pochemu-komanda-rastyot-a-fichi-vyhodyat-medlennee?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pochemu-komanda-rastyot-a-fichi-vyhodyat-medlennee</guid>
      <description><![CDATA[<p>Один день на прямой SQL или три на разделение слоёв? Разбираем связность кода, адаптеры и технический долг: как сократить объём будущих правок?</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pochemu-komanda-rastyot-a-fichi-vyhodyat-medlennee">Почему команда растёт, а фичи выходят медленнее</a>»</p>]]></description>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Архитектура приложений]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 30 Sep 2026 09:49:10 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вы меняете сервис проверки паспортов, а вместе с ним приходится переписывать обработку заявки. Условия, по которым заявка проходит дальше, остались прежними, но работы всё равно прибавилось.</p><h2>Почему больше разработчиков не означает меньше работы</h2><p>Мы в Centicore вместе с нашим коллегой разбираем, почему с ростом команды разработка может замедляться. Он около пяти лет занимался системной архитектурой крупных систем, а затем перешёл в управление.</p><p>Например, два корпоративных проекта. В первом работали 50 человек, и команда с трудом завершила работу за год. Во втором людей было втрое меньше, а результат, по его оценке, оказался заметно лучше.</p><p>Рост команды сам по себе не уменьшает объём изменений, необходимых для каждой фичи. Разберём одну из причин, по которой этот объём растёт: сильную связность модулей внутри сервиса. При сильной связности небольшое изменение затрагивает сразу несколько компонентов. Разработчик меняет нужную логику, затем связанные с ней интеграции, проверяет соседний код и исправляет тесты. Задача растёт, хотя бизнес просил примерно то же, что и раньше. Новый сотрудник получает ту же систему зависимостей. Ему тоже нужно разобраться, что затронет его правка. Дополнительные люди могут взять часть работы, но сам объём изменений от этого не сокращается.</p><p>Разделение на микросервисы эту проблему само по себе не решает. Даже внутри отдельного сервиса бизнес-правила могут быть тесно связаны с конкретной базой данных или внешним API. Поэтому полезно посмотреть, сколько кода приходится менять из-за одной новой интеграции.</p><h2>Как проверка паспорта затрагивает обработку заявки</h2><p>Возьмём условную систему, которая проверяет данные клиента перед тем, как передать заявку дальше. В упрощённом примере бизнес-логике нужны два результата: действителен ли паспорт и отсутствует ли человек в нежелательных списках.</p><p>Допустим, сначала внешний сервис принимает только номер паспорта. Затем команда переходит на другой сервис, которому нужны ещё ФИО и код подразделения. Меняются состав запроса, протокол взаимодействия и формат ответа. При этом условия прохождения заявки остаются прежними. Если вызов провайдера и разбор его ответа находятся прямо в коде обработки заявки, менять придётся этот код. В одном месте оказались две причины для изменений: новые бизнес-правила и новый способ получения данных.</p><p>Из-за этого задача по замене интеграции затрагивает сценарий целиком. Чтобы ограничить объём правок, нужно отделить получение результата проверки от решения о том, что делать с заявкой.</p><h2>Как отделить бизнес-правила от технических деталей</h2><p>В чистой и луковой архитектуре бизнес-логика находится в центре, а работа с базами данных, внешними сервисами и пользовательским интерфейсом вынесена наружу. У этих подходов есть различия, но общий для нашего примера принцип один: зависимости исходного кода направлены к бизнес-логике.</p><p>В модели чистой архитектуры выделяют четыре области:</p><ul><li>Сущности. Основные бизнес-правила и данные предметной области.</li><li>Варианты использования. Сценарии приложения, которые организуют работу с сущностями.</li><li>Адаптеры интерфейсов. Контроллеры, презентеры и шлюзы, которые связывают сценарии с внешним миром и преобразуют данные.</li><li>Фреймворки и драйверы. Внешние технические средства, включая веб-фреймворк и базу данных.</li></ul><p>Зависимость здесь означает, что один модуль использует определения другого: например, его интерфейсы или классы. Контроллер может обращаться к сценарию приложения. Сценарий при этом не должен зависеть от конкретного контроллера или реализации доступа к базе.</p><p>Для внешней операции сценарий использует интерфейс, определённый во внутренней части приложения. Внешний адаптер реализует этот интерфейс. Так сценарий может получить результат проверки паспорта, не обращаясь в своём коде к конкретному провайдеру. При выполнении программы запрос всё равно дойдёт до внешнего сервиса. Правило описывает зависимости кода, а порядок вызовов во время выполнения может быть другим.</p><p>Поэтому проверить архитектуру можно по конкретному месту: от чего зависит сценарий обработки заявки? Если ему нужен класс клиента определённого API или конкретный модуль доступа к БД, техническая реализация всё ещё влияет на его устройство.</p><h2>Что останется прежним при смене провайдера</h2><p>Вернёмся к паспорту. Адаптер получает необходимые данные, собирает запрос к внешнему сервису и преобразует ответ в результат, с которым работает приложение. В нашем примере это те же два признака: паспорт действителен, человек отсутствует в нежелательных списках.</p><p>При смене провайдера команда пишет новый адаптер. В нём будут другой запрос и другой разбор ответа. Сценарий обработки заявки продолжит получать результат проверки в прежнем виде.</p><p>Это работает, пока сохраняются бизнес-правила и приложению доступны нужные данные. Если ФИО или код подразделения раньше вообще не собирали, потребуется изменить и получение этих данных. Один адаптер не решит эту часть задачи. А если изменились условия, по которым заявка проходит дальше, правки понадобятся и в бизнес-логике.</p><p>С базой данных действует тот же принцип. Сценарий обращается к интерфейсу репозитория, а конкретная реализация работает с БД через SQL, ORM или собственный модуль доступа к данным. При переходе с PostgreSQL на Oracle это помогает сохранить код бизнес-правил, если их удалось отделить от особенностей конкретной БД.</p><p>Практический результат такого разделения виден в составе задачи: при смене внешнего сервиса разработчик меняет интеграцию, а правила обработки заявки остаются прежними, если требования к ним не менялись.</p><h2>Почему на старте получается три дня вместо одного</h2><p>В нашем примере простую реализацию можно собрать за день: контроллер принимает запрос, запускает скрипт с прямым SQL, и тот обращается к базе. На вариант с разделением бизнес-логики, интерфейсов и доступа к данным в том же примере уходит три дня.</p><p>Это условные оценки для объяснения компромисса. Они показывают, откуда берутся дополнительные затраты на старте нового сервиса: команда определяет границы модулей и способы их взаимодействия. Когда структура уже готова, её не приходится заново создавать для каждой следующей задачи.</p><p>Для короткоживущего MVP, который действительно собираются выбросить, прямой путь может быть оправдан. В системе, которую будут развивать и подключать к новым сервисам, нужно учитывать стоимость следующих изменений.</p><p>Допустим, после первого релиза меняется интеграция. В одном варианте команда правит адаптер. В другом ей приходится разбираться ещё и с обработкой заявки, потому что детали провайдера встроены в сценарий. Первоначальная экономия времени начинает оборачиваться дополнительной работой.</p><p>По мере развития системы таких связей может становиться больше. Тогда исправление затрагивает анализ требований, код и тесты сразу нескольких компонентов. Обсуждать архитектуру полезно через этот объём работы: какие изменения ожидаются и где их придётся делать.</p><h2>Как сохранить границы, когда фича нужна вчера</h2><p>На практике разделение слоёв часто проигрывает срочной задаче. Можно выделить несколько причин: жёсткие дедлайны, меняющиеся требования, нехватку опыта, проблемы с документацией и онбордингом. Под давлением команда выбирает короткий путь, а вернуться к нему позже становится отдельной задачей.</p><p>В разборе есть три способа, рассказывающие, как с этим работать.</p><ul><li>Подготовить шаблон сервиса. Заранее задать структуру для сущностей, сценариев и репозиториев, а также интерфейсы между ними. Тогда разработчику будет проще добавить логику в готовую структуру. При этом команде всё равно нужно понимать назначение границ и соблюдать их.</li><li>Зафиксировать технический долг. Если ради срока пришлось связать сценарий с конкретной интеграцией, описать принятое решение и запланировать рефакторинг. Так временное упрощение останется видимой задачей.</li><li>Объяснять затраты на конкретных изменениях. Обсудить с бизнесом, что произойдёт при замене сервиса проверки: где потребуется новый запрос, какие части приложения останутся прежними и почему команда тратит время на их разделение сейчас.</li></ul><p>Последний пункт требует участия тимлида и менеджера. Им нужно связать дополнительные затраты на текущую задачу с понятной будущей работой. В примере с паспортом такой аргумент уже есть: смена провайдера не должна заставлять команду заново разбираться с неизменившимися правилами обработки заявки.</p><h2>С чего начать в существующем проекте</h2><p>Начать можно с зависимостей внутри одного сервиса. Посмотрите, какие сценарии напрямую используют конкретную БД или внешний API и какие правки это уже вызывает. На примере проверки паспорта задача состоит в том, чтобы отделить правила обработки заявки от получения результатов проверки.</p><p>После такого разделения у следующей замены провайдера должна появиться понятная область изменений. Если бизнес-правила остались прежними, а команде снова приходится переписывать их реализацию, граница между сценарием и интеграцией ещё требует работы.</p>]]></content:encoded>
    </item>
    <item>
      <title>7 стратегий модернизации легаси: что рефакторить, переносить, заменять или переписывать</title>
      <link>https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za</link>
      <comments>https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za</guid>
      <description><![CDATA[<p>7 стратегий модернизации легаси: когда оставить как есть, а когда рефакторить, переносить или переписывать. Практический гайд.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/7-strategij-modernizacii-legasi-chto-refaktorit-perenosit-za">7 стратегий модернизации легаси: что рефакторить, переносить, заменять или переписывать</a>»</p>]]></description>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Архитектура ПО]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 25 Aug 2026 08:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Переписать проект с нуля — первое, что придет вам в голову, но по факту это месяцы работы без новых функций и без гарантии, что новая версия окажется лучше старой. Часто лучше обновить стек, разобрать один проблемный модуль или постепенно вынести часть логики в отдельный сервис.</p><h2>Сначала понять, нужно ли трогать систему</h2><p>Если приложение выдерживает нагрузку, получает исправления безопасности и не мешает выпускать новые функции, спешить с модернизацией незачем. Работает и работает.</p><p>Проблемы начинаются, когда разработчик меняет один модуль и роняет соседние, релизы начинают выкатываться реже, а поддержка системы забирает больше времени. Или большой монолит может долго запускаться, плохо масштабироваться и больно реагировать на сбой внутри одного модуля. Причин бывает сколько угодно, но стратегию подбирают под каждую отдельно. Где-то хватит новой версии фреймворка, где-то придётся отделять авторизацию, где-то — выносить отчёты в свой сервис.</p><p>Поэтому начинают с проблемы:</p><ul><li>старый стек больше не получает обновления;</li><li>новые функции делаются слишком долго;</li><li>систему трудно тестировать или масштабировать;</li><li>сбой одного модуля затрагивает всё приложение;</li><li>на поддержку уходит всё больше людей и рабочих часов.</li></ul><p>Всё это можно измерить, проверить и объяснить бизнесу. Дальше — варианты.</p><h2>1. Оставить систему как есть</h2><p>Иногда самое разумное решение состоит в том, чтобы ничего не менять.</p><p>Допустим, приложение решает одну понятную задачу, нагрузка на него почти не растёт, а новые функции добавляют пару раз в год. Переписывать такую систему только из-за возраста кода смысла мало: команда потратит квартал, а бизнес получит примерно ничего.</p><p>Следить за ней всё равно придётся. Обновления безопасности нужно ставить, резервные копии проверять, а документацию по запуску, интеграциям и восстановлению держать в актуальном состоянии.</p><p>Заранее стоит договориться, какое событие станет сигналом к началу модернизации. Например:</p><ul><li>платформа или библиотека перестала получать исправления безопасности;</li><li>поддержка стала занимать больше времени, чем раньше;</li><li>система перестала выдерживать нагрузку;</li><li>нужную бизнесу функцию нельзя добавить без большой переделки;</li><li>в команде не осталось людей, которые понимают код.</li></ul><p>Пока ничего из этого не случилось, систему оставляют в текущем виде.</p><h2>2. Вывести ненужную часть из эксплуатации</h2><p>Иногда часть легаси-системы уже никому не нужна: старый модуль, забытый API или интеграция с сервисом, который давно закрылся. Такой компонент проще отключить, чем поддерживать его дальше и тем более куда-то переносить.</p><p>Сначала придётся разобраться с зависимостями, потому что команда может просто не знать, что модуль дёргают соседние сервисы или другой отдел. Перед отключением стоит:</p><ul><li>проверить логи и запросы к компоненту;</li><li>найти связанные сервисы и задания;</li><li>узнать, какие команды пользуются его данными;</li><li>подготовить способ быстро вернуть его обратно.</li></ul><p>После проверки компонент выключают на тестовый период, и если за это время ошибок не появилось, код и инфраструктуру удаляют. Данные, которые ещё могут понадобиться, перед этим складывают в архив.</p><h2>3. Обновить стек без смены архитектуры</h2><p>Система остаётся монолитом: команда не делит её на микросервисы и не трогает основные связи между модулями. Меняется техническая начинка, то есть язык, фреймворк, библиотеки, база данных или среда выполнения.</p><p>Например, приложение переводят на поддерживаемую версию .NET, но оно по-прежнему остаётся одним приложением. Такой вариант подходит, когда архитектура со своими задачами пока справляется, а болит именно устаревшая технология.</p><p>Просто поменять номер версии в конфиге обычно не получается, потому что старые библиотеки отказываются работать с новым фреймворком, а часть API меняется или исчезает вовсе. Поэтому сначала собирают список зависимостей и смотрят, что из этого вообще можно обновить.</p><p>Дальше компоненты обновляют по очереди, прогоняя тесты после каждого шага. Отдельно проверяют интеграции, работу с базой данных и поведение под нагрузкой. Новую версию сначала разворачивают в тестовой среде, а для выката в продакшен готовят план отката.</p><p>Проблемы самого монолита при этом никуда не денутся: если модули сильно связаны между собой или приложение плохо масштабируется, одним обновлением стека дело не обойдётся.</p><h2>4. Перенести систему на другую платформу</h2><p>Здесь код приложения почти не меняют, меняется место, где оно работает. Например, систему можно перенести с физических серверов на виртуальные машины или в облако. Другой вариант — запустить приложение в контейнерах. Архитектура при этом остаётся прежней: монолит не превращается в набор микросервисов.</p><p>Перед переносом нужно проверить требования к процессору, памяти, дискам и сети. Ещё нужно учесть внешние сервисы, настройки безопасности и доступ к базе данных. Затем систему разворачивают на новой платформе и проверяют под рабочей нагрузкой.</p><p>Такой перенос сам по себе не ускоряет приложение. Если проблема находится в коде или базе данных, смена инфраструктуры её не решит. Зато команда может отказаться от устаревших серверов и упростить дальнейшую поддержку системы.</p><p>Старую среду лучше отключать только после проверки новой. Если при запуске возникнут ошибки, трафик можно будет вернуть обратно.</p><h2>5. Заменить готовым продуктом</h2><p>Некоторые внутренние системы дешевле заменить готовым решением. Типичная ситуация: компания годами поддерживает собственный сервис, хотя продукт с теми же функциями давно можно купить или взять по подписке.</p><p>Начинают со сравнения расходов. С одной стороны, зарплаты разработчиков, серверы, исправление ошибок и обновления своей системы. С другой, лицензия, внедрение, перенос данных и доработки готового продукта.</p><p>Дальше сверяют функции, потому что готовое решение может не поддерживать часть внутренних процессов или нужные интеграции. Команда составляет список обязательных возможностей и проверяет их до перехода, пока договор ещё не подписан.</p><p>Отдельно разбираются с данными: что переносить, в каком формате и как проверить результат. Старую систему держат включённой, пока пользователи не начнут работать в новой, а данные и интеграции не пройдут проверку.</p><h2>6. Рефакторить по частям</h2><p>При рефакторинге поведение системы снаружи сохраняется, а внутри код меняется: команда упрощает структуру, разделяет связанные компоненты, убирает дублирование и обновляет отдельные зависимости.</p><p>Переписывать для этого весь проект не нужно. Сначала выбирают модуль, который часто меняется, собирает много ошибок или мешает добавлять новые функции. Затем фиксируют его текущее поведение тестами и только после этого берутся за код.</p><p>Работу делят на небольшие этапы, и после каждого запускают тесты, чтобы убедиться, что система ведёт себя как раньше. Если что-то всё-таки сломалось, маленькое изменение откатить куда проще, чем трёхнедельный коммит.</p><p>Чтобы выбрать модули, нужно понимать их зависимости, частоту изменений и роль в бизнес-процессах. Когда внутри компании таких специалистов нет, зовут внешнюю команду.<a href="https://centicore.ru/services/legacy-modernization/"> Centicore</a> занимается модернизацией легаси-кода, баз данных и инфраструктуры, в услугу входят рефакторинг кодовой базы и обновление технологического стека.</p><h2>7. Переписать систему постепенно</h2><p>Систему можно заменять по одному модулю за раз, оставляя остальное работать как прежде. Подход называется Strangler Fig, по имени фикуса-душителя, который оплетает дерево и со временем занимает его место.</p><p>Новые функции сразу пишут на новом стеке, какое-то время старая и новая части работают параллельно, поэтому запросы приходится направлять в нужную версию системы. Новый сервис при этом может ходить в прежнюю базу данных, и если такая схема начинает мешать работе или масштабированию, данные позже переезжают в отдельную БД.</p><p>Когда функция переехала, старый модуль отключают. Монолит понемногу уменьшается, а новая система забирает его задачи.</p><h2>Как выбрать стратегию</h2><p>Для всей системы один подход выбирать необязательно. Стабильный модуль можно не трогать, старую интеграцию удалить, а авторизацию постепенно вынести из монолита.</p><p>Сначала каждую часть системы проверяют по нескольким критериям:</p><ul><li>как часто в ней меняют код;</li><li>сколько времени уходит на поддержку;</li><li>от каких сервисов и баз данных она зависит;</li><li>есть ли автоматические тесты;</li><li>сколько времени она может не работать;</li><li>хватает ли у команды людей и знаний для изменений.</li></ul><p>Рефакторинг подойдёт коду, который трудно менять, хотя заменять его целиком нет смысла. Когда готовый продукт закрывает те же задачи, считают стоимость перехода. А если архитектура уже мешает выпускать функции и масштабировать систему, отдельные модули переписывают постепенно.</p><h2>Что подготовить до начала работ</h2><p>Работу делят на этапы, и каждый должен давать результат, который можно проверить и выпустить отдельно. Задачу длиной в несколько месяцев тяжело оценивать, тестировать и откатывать.</p><p>До начала изменений нужно:</p><ul><li>составить список модулей, сервисов, баз данных и интеграций;</li><li>найти команды, которые пользуются системой;</li><li>проверить автоматические и ручные тесты;</li><li>измерить текущую нагрузку и время ответа;</li><li>определить порядок выпуска изменений;</li><li>подготовить откат к рабочей версии.</li></ul><p>Тестировать отдельные модули недостаточно. После изменений проверяют интеграции, работу с базой данных, нагрузку и основные пользовательские сценарии.</p><h2>Итого</h2><p>Легаси необязательно переписывать целиком. Сначала стоит понять, какая именно часть системы мешает работать и почему.</p><p>Неиспользуемый код удаляют, устаревший стек обновляют, а систему при необходимости переносят на новую платформу без изменения архитектуры. Отдельные модули рефакторят, заменяют готовым продуктом или переписывают по частям.</p><p>Обычно в одном проекте сочетаются сразу несколько стратегий. Так систему меняют постепенно, продолжают выпускать новые функции и проверяют результат после каждого этапа.</p>]]></content:encoded>
    </item>
    <item>
      <title>Почему ИИ-агент тупеет на большом проекте</title>
      <link>https://tproger.ru/articles/pochemu-ii-agent-tupeet-na-bolwom-proekte</link>
      <comments>https://tproger.ru/articles/pochemu-ii-agent-tupeet-na-bolwom-proekte?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/pochemu-ii-agent-tupeet-na-bolwom-proekte</guid>
      <description><![CDATA[<p>Как устроено контекстное окно LLM, почему агенты деградируют на больших кодовых базах и как бороться: chunking, RAG, суммаризация и выбор модели под задачу.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/pochemu-ii-agent-tupeet-na-bolwom-proekte">Почему ИИ-агент тупеет на большом проекте</a>»</p>]]></description>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 17 Jul 2026 09:24:48 GMT</pubDate>
      <content:encoded><![CDATA[<p>Представьте ситуацию: еще вчера ваш ИИ-агент писал новые API за пару минут, делал рефакторинг и находил ошибки точно под микроскопом. Но проект растет: появляются десятки новых микросервисов, сотни тысяч строк кода и куча библиотек. И наступает тот самый момент, когда ИИ начинает вести себя странно.</p><p>Агент забывает, что было пять минут назад, предлагает удалить сто раз проверенную функцию и генерирует код, который противоречит архитектуре. Здесь многие разработчики подумают: «модель совсем испортилась», но проблема не в модели, а в контексте.</p><p>Современные LLM устроены так, что качество ответа зависит не только от модели, но и от того, какую информацию она получает в запросе. Поэтому по мере увеличения кодовой базы самым сложным для модели становится управление контекстом. Уже даже появился термин — context engineering (передаем привет промт-инжинирингу), и сейчас команды, работающие над агентами, все больше внимания уделяют правильному формированию контекста.</p><p>В статье рассказываем, почему теперь недостаточно просто написать хороший промпт.</p><h2>Контекстное окно — не равно память</h2><p>Многие разработчики воспринимают контекстное окно как память модели, но это не совсем так. У LLM нет долговременной памяти в привычном смысле. При каждом запросе модель получает некоторый объем информации: промтп, историю диалога, файлы, ссылки, сообщения пользователя и найденные в поиске части кода. Это превращается в последовательность токенов, которая называется <b>контекстным окном.</b></p><p>После завершения генерации модель забывает абсолютно все в рамках конкретной рабочей сессии — следующий запрос запускает процесс заново. Поэтому агент, который кажется «умным», на самом деле каждый раз снова собирает картину проекта из того набора данных, который ему передали.</p><p>Если нужного файла нет в контексте — для модели он буквально не существует.</p><h3>Размер окна — не единственная проблема</h3><p>В базовой модели ChatGPT 5 — 16 000 токенов, в платных число доходит до 400 000. Если вы используете API-платформы, такие как <a href="https://routerai.ru" rel="nofollow">RouterAI</a>, то вам доступен 1 млн токенов — это примерно 750 000 слов. Вот как модели переводят слова в токены:</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-16/8d4f29af-9138-428f-b6af-9cdd054461f6.webp" alt="" /></figure><p>Действительно, миллион токенов — огромное количество, но есть важный нюанс: возможность прочитать миллион токенов совсем не означает, что модель способна одинаково хорошо использовать каждую их часть. Это подтверждает исследование <a href="https://arxiv.org/pdf/2307.03172">Lost in the Middle</a> 2023 года: авторы показали, что модели с большим контекстом лучше всего обрабатывают информацию, которая находится в начале или конце входного текста, а то, что в середине, можно сказать, ей упускается.</p><p>Например: агент получил 150 файлов проекта, логи, документацию, описание задачи, результаты. Формально вы положили в контекст все нужные входные данные, но далеко не факт, что модель одинаково обработает каждые из них. Считайте, что в этот момент она ищет иголку в стоге сена.</p><h3>Почему большие проекты особенно сложные</h3><p>Если текстовый файл обычно читается последовательно, то код — нет. Чтобы изменить один метод в бэке, агент будет искать информацию по самым разным частям репозитория, поскольку UI может лежать в одном пакете, реализация — в другом, а бизнес-логика и вовсе распределена между несколькими сервисами, и ещё, конечно, тесты.</p><p>Здесь важно учитывать, что разработчик выстраивает архитектуру и карту проекта постепенно (и зачастую не в одиночку), а модель вынуждена анализировать ее практически мгновенно. Следовательно, чем больше проект, тем выше вероятность того, что важная зависимость банально не попадет в текущий контекст. Именно поэтому агенты начинают использовать старый UI, дублировать код, нарушать архитектуру или генерировать ломающие друг друга изменения.</p><p>Вам может показаться, что агент стал менее сообразительным и перестал вас понимать, но на самом деле он перестал видеть достаточно информации для правильной реализации того или иного решения.</p><h3>«Так я могу взять и загрузить весь репозиторий»</h3><p>Логика в этом есть, но на практике это почти никогда не работает. Во-первых, даже большое контекстное окно остается ограниченным: огромный репозиторий наверняка перевесит сотни тысяч и иногда миллионы токенов. Во-вторых, стоимость использования одной нейросети растет вместе с объемом входных данных: чем больше токенов загружается в модель, тем выше задержка и стоимость каждого запроса.</p><p>Но самая неприятная проблема — информационный шум, который создается при большом контексте. Представьте: перед код-ревью вам дали не один пакет или pull request, а весь репозиторий — вы потратите на это огромное количество сил и времени. С ИИ происходит примерно то же самое.</p><h2>Как бороться с деградацией контекста</h2><p>Может показаться, что современные AI-инструменты научились работать с огромными репозиториями — этим славятся GitHub Copilot, Claude и Cursor. Но на самом деле мало кто пытается загрузить в модель репозиторий целиком — поскольку важнее не размер контекстного окна, а качество информации. Именно поэтому нужно знать несколько архитектурных приемов.</p><h3>Chunking</h3><p>Это первый и самый очевидный прием: вместо хранения одного репозитория в виде одного огромного текста его заранее разбивают на смысловые фрагменты (chunks).</p><p>Казалось бы, все просто — разбить код по фиксированному количеству строк. Например, у вас 400 строк кода, которые можно разрезать по 100, но такой путь с большой вероятностью окажется ложным, поскольку смысловые фрагменты могут отделиться друг от друга, и модель получит две бессмысленные половины.</p><p>Поэтому в современных инструментах есть структурный чанкинг. В нем границы определяются элементами синтаксического дерева (AST — Abstract Syntax Tree): классом, функцией, UI, модулем, SQL-запросом и т.д. Такой подход позволяет сохранить логическую целостность кода.</p><p>Однако такой «трюк» не пройдет с репозиториями, например, на миллион строк. Несмотря на то, что каждый чанк содержит всего несколько сотен токенов, самих строк будет несколько тысяч, и модель не сможет прочитать все.</p><p>И здесь на помощь приходит RAG.</p><h3>Retrieval Augmented Generation</h3><p>Сейчас RAG чаще ассоциируют с чат-ботами, которые ищут ответы в документации. Но принцип работает абсолютно так же и для кода.</p><p>Вот пример классической архитектуры:</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-16/f91b0a19-79d8-4c6d-81e8-9987f5a729d0.webp" alt="" /></figure><p>То есть модель вообще не знает о существовании большей части репозитория. Она видит только те части проекта, которые поисковый механизм признал наиболее полезными. Поэтому именно качество поиска влияет на итоговую выдачу сильнее, чем выбор самой LLM.</p><p>Если в процессе retrieval нашлись не правильные файлы, даже GPT-5, Claude 4 или Gemini 2.5 будут уверенно рассуждать на основе неверных данных. Поэтому современные агенты все больше напоминают поисковые системы, а не просто оболочку вокруг LLM.</p><p>Но есть еще одна проблема. Например, агент анализирует задачу уже полчаса, за которые он изучил десятки файлов, сделал поисковые запросы, протестировал, сгенерировал новые модули и даже успел что-то спросить у пользователя. Все эти данные тоже начинают занимать место в контекстном окне и история диалога становится слишком большой. И здесь на помощь приходит суммаризация.</p><h3>Суммаризация</h3><p>Сейчас один из самых популярных методов — иерархическая структуризация. Вместо того, чтобы хранить всю историю переписки, агент периодически делает краткое описание, освобождая контекстное окно:</p><p>Реализовал новую версию UserRepository, обновил миграцию, синхронизировал тесты с новой схемой.</p><p>Сегодня агенты используют несколько уровней суммаризации: отдельного файла, задачи, проекта, предыдущих запросов и так далее.</p><p>Именно поэтому разработчики сейчас так любят Claude — создатели не просто сделали огромное контекстное окно, а пошли дальше и научили нейронку динамически искать файлы, повторно читать код при необходимости, автоматически сокращать контекст и копить знания. Получается, что модель постоянно собирает информацию под конкретную задачу.</p><figure><img src="https://media.tproger.ru/user-uploads/99633/2026-07-16/2a2243ba-2fad-41bd-b4d9-1bb221376a55.webp" alt="" /><figcaption>Схема работы кодового агента</figcaption></figure><h2>Когда большой контекст необходим</h2><p>Вот основные кейсы, когда переход на модель с большим контекстным окном полностью оправдан:</p><ul><li>Большой рефакторинг. Например, команда решила перейти с одной библиотеки на другую, а код лежит в нескольких сотнях файлов. Модели нужно одновременно понимать старую архитектуру и предложить новую реализацию, которая не сломает приложение. В этом случае большой контекст поможет снизить количество итераций поиска.</li><li>Монорепозитории. В крупных компаниях, например, в Google, один репозиторий может содержать десятки и даже сотни сервисов. Даже при точном поиске ограниченность узкого контекстного окна будет большой проблемой.</li><li>Генерация больших изменений. Суть примерно та же самая, что и с рефакторингом: модель должна переписать кучу файлов, сохранить единый стиль, не нарушить связи и обновить тесты — большой контекст просто поможет удерживать всю информацию о задаче.</li></ul><p>Но даже в этих сценариях нельзя забывать про RAG, суммаризацию и точность поиска.</p><h2>Что делать, если одной модели недостаточно</h2><p>Если мы говорим о повседневных задачах, например, автодополнение кода или написание небольшой функции, то здесь важнее будет стоимость и скорость ответа, поэтому часто модели с небольшим контекстом будет достаточно.</p><p>Но в случае большого рефакторинга, монорепозиториев и других крупных проектов, где необходимо сохранить длинную историю диалога, нужен большой контекст. Поэтому многие команды внедряют архитектуру, в которой модель можно заменить без переписывания логики. Так, если сегодня вы сидите на Claude, а завтра GitHub выпустит прорывную модель, переключение должно занять минуты и затронуть как можно меньше кода.</p><p>Именно по этой причине сейчас появляется много сервисов, которые предоставляют единый доступ к различным моделям через API. Например, в RouterAI можно переключить агента на другую модель с большим контекстным окном, не меняя клиентский код и интеграцию с API. Это не решает проблему формирования контекста автоматически, но заметно упрощает эксперименты с разными моделями и позволяет выбирать инструмент под конкретный сценарий, а не подстраивать приложение под особенности каждого отдельного провайдера.</p><h2>Итоги</h2><p>Когда разработчикам кажется, что модель начинает «глупеть», проблема часто не в самой LLM, а в контексте, с которым она работает. И сейчас качество определяется не только количеством токенов, но и тем, как устроены RAG, суммаризация, чанкинг, а главное — поиск. Таким образом, нужно прокачивать и промпт-инжиниринг, и контекст-инжиниринг, чтобы научить модель видеть именно то, что необходимо для решения конкретной задачи.</p>]]></content:encoded>
    </item>
    <item>
      <title>Технический долг в деньгах: как считать ROI рефакторинга легаси-системы</title>
      <link>https://tproger.ru/articles/tehnicheskij-dolg-v-dengah-kak-schitat-roi-refaktoringa-legasi</link>
      <comments>https://tproger.ru/articles/tehnicheskij-dolg-v-dengah-kak-schitat-roi-refaktoringa-legasi?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Дмитриев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/tehnicheskij-dolg-v-dengah-kak-schitat-roi-refaktoringa-legasi</guid>
      <description><![CDATA[<p>Как перевести техдолг в деньги: отделяем от багов и новых требований, считаем текущие потери и обосновываем рефакторинг бизнесу через скорость, предсказуемость и риски.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/tehnicheskij-dolg-v-dengah-kak-schitat-roi-refaktoringa-legasi">Технический долг в деньгах: как считать ROI рефакторинга легаси-системы</a>»</p>]]></description>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Legacy]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 30 Jun 2026 11:14:40 GMT</pubDate>
      <content:encoded><![CDATA[<p>Для бизнеса это выглядит как внутренняя проблема разработки. Система работает, пользователи продолжают пользоваться продуктом, релизы выходят. Поэтому рефакторинг часто проигрывает задачам, которые напрямую связаны с продуктовым роадмапом.</p><p>В статье разберём этот эффект на примере легаси-систем, с которыми работает Centicore Group. Компания занимается модернизацией устаревшего кода, баз данных и инфраструктуры, а также системным и бизнес-анализом, разработкой ПО и контролем качества.</p><p>Дальше узнаете, как перевести техдолг легаси-системы в деньги: отделить его от багов и новых требований, оценить объём работ, увидеть текущие потери и объяснить, когда модернизация становится дешевле, чем дальнейшая разработка поверх старой архитектуры.</p><h2>Что считаем техдолгом</h2><p>Чтобы посчитать ROI рефакторинга, сначала нужно определить, что именно команда считает техническим долгом. Без этого в один список попадут баги, старые библиотеки, слабые тесты или желание сменить стек. У таких задач разные причины, цена и источники бюджета, поэтому их нельзя приоритизировать как одну категорию.</p><p>Термин «технический долг» ввёл программист Уорд Каннингем в 1992 году. Он использовал финансовую метафору: команда может выбрать более быстрое техническое решение, получить выгоду сейчас, а позже заплатить за это доработкой и дополнительными расходами.</p><p>В этой статье будем использовать узкое рабочее определение: <b>технический долг — это осознанное отклонение от технических соглашений команды ради понятной выгоды.</b> Команда понимает целевое состояние, но пока этого не делает, чтобы быстрее выпустить фичу, проверить гипотезу или уложиться в ограничение по срокам.</p><p>Из этого определения следует, что не каждую техническую проблему стоит записывать в техдолг:</p><ol><li>Баг — это ошибка в реализации. Техдолга здесь нет: никто не принимал осознанного решения сделать хуже. Поэтому баги относятся к дефект-менеджменту и финансируются из бюджета на поддержку, а не из бюджета на рефакторинг.</li><li>Желание применить новую технологию — это новое требование к продукту. Команда хочет заменить PostgreSQL на что-то другое или перейти с REST на gRPC. Если это обосновано и полезно — отлично, но это отдельная задача со своей оценкой и своим источником финансирования.</li><li>Устаревший код без измеримых последствий — тоже не долг. Если модуль написан пять лет назад, но команда работает с ним без дополнительных затрат времени, он никого не тормозит и не создаёт рисков — переписывать его ради переписывания не нужно. Техдолг возникает в момент, когда команда признаёт: вот это решение замедляет нас, и мы можем точно сказать, как именно.</li></ol><p>У каждой категории своя логика и бюджет. Баги относятся к дефект-менеджменту, новые технологии — к развитию продукта, обучение — к развитию команды. Если всё сложить в один бэклог и назвать техдолгом, ресурсы будут расходоваться непрозрачно, а разговор с бизнесом снова сведётся к просьбе выделить время на внутренние задачи.</p><h2>Как техдолг превращается в расходы</h2><p>После фиксации техдолга у команды появляются два параметра: <b>объём работ и текущие потери</b>. Они нужны, чтобы отделить саму задачу по исправлению техдолга от расходов, которые команда и так тратит каждый спринт.</p><p><b>Объём работ</b> — это всё, что нужно сделать, чтобы вернуться в целевое состояние. Раздробить монолитный модуль, заменить самописную библиотеку на поддерживаемую, переписать слой работы с БД. Объём работ оценивается в часах или стори-поинтах — это конечная сумма, которую команда выплатит один раз.</p><p><b>Текущие потери</b> — это то, что команда теряет каждый спринт, пока долг не закрыт. Они накапливаются постепенно и часто не видны явно, пока их не начинают считать. Несколько типичных примеров: медленная сборка съедает по 20 минут на каждый деплой при восьми деплоях в день — это больше двух часов потерь ежедневно на каждого разработчика, который её ждёт. Запутанная структура модуля добавляет час к онбордингу каждого нового члена команды и увеличивает время оценки задач. Отсутствие тестов на критическом участке означает ручную проверку при каждом релизе.</p><p>Текущие потери важнее объёма работ для обоснования приоритета. Именно они показывают, сколько стоит бездействие — и растут с каждой итерацией: кодовая база вокруг расширяется, зависимостей становится больше, а проблемный участок затрагивает всё больше задач.</p><p>Откуда берётся бюджет на закрытие долга? На практике его берут из нескольких источников: остаток капасити после основных задач, риск-буфер проекта, иногда закладывают в оценку соседних фич. Последний вариант наименее прозрачный, но встречается чаще всего.</p><h2>Какие задачи по техдолгу считать через ROI</h2><p>Не каждую задачу по техдолгу нужно считать через ROI. Часть работы должна быть встроена в обычный процесс разработки: тесты к изменяемому коду, обновление документации, небольшие правки рядом с текущей задачей.</p><p>Через ROI стоит считать задачи, которые конкурируют с фичами за время команды. У таких задач есть цена, влияние на скорость разработки и понятный конфликт с продуктовым планом. Например:</p><ul><li>рефакторинг проблемного модуля,</li><li>замена устаревшей зависимости,</li><li>переработка слоя интеграций,</li><li>доработка тестового покрытия на критическом участке.</li></ul><p>Отдельно стоят системные решения — их нельзя решать на уровне одного спринта. Для них нужна отдельная оценка, архитектурное решение и согласование с бизнесом. Например:</p><ul><li>вывод старых сервисов из эксплуатации,</li><li>изменение требований к отказоустойчивости,</li><li>обновление платформы,</li><li>пересмотр архитектурных ограничений.</li></ul><p>После такого отбора остаются задачи, которые действительно нужно считать. Следующий шаг — понять, где именно легаси будет создавать дополнительные расходы.</p><h2>Где искать расходы в легаси перед расчётом ROI</h2><p>После фиксации техдолга нужно понять, где именно он влияет на стоимость работы. Для легаси-системы это не всегда весь продукт целиком: чаще дополнительные затраты создают отдельные модули, интеграции или участки инфраструктуры.</p><ul><li>Сначала проверяют зоны частых изменений. Если команда регулярно дорабатывает один и тот же модуль, а перед каждой задачей тратит время на разбор старой логики, такой долг стоит учитывать в первую очередь, потому что он возвращается в каждой новой задаче.</li><li>Затем оценивают поддержку и эксплуатацию. Старые решения могут увеличивать время на инциденты, ручные операции, деплой, проверку интеграций и передачу знаний внутри команды. Эти затраты не всегда явно видны в разработке фич, но они каждый раз сокращают общий ресурс команды.</li><li>Отдельно смотрят ограничения для будущего развития. Если текущая архитектура мешает новым интеграциям, требованиям к безопасности, масштабированию или изменению бизнес-логики, долг влияет уже не только на поддержку, но и на продуктовый план.</li></ul><p>В результате у вас появляется список участков, где легаси создаёт дополнительные расходы, например: замедляет разработку, усложняет поддержку, повышает риски или ограничивает будущие изменения. Этот список нужен для следующего шага — оценки ROI рефакторинга.</p><h2>Как считать ROI</h2><p>Полностью оцифровать каждую задачу по техдолгу в рублях на практике невозможно: слишком много переменных и слишком высокая стоимость самого подсчёта. Но для принятия решений точная оцифровка и не нужна. Нужно уметь сравнивать задачи между собой и понимать, когда рефакторинг выгоднее очередной фичи.</p><p><b>Шаг 1. Посчитайте текущие потери.</b> Зафиксируйте, сколько дополнительного времени команда тратит из-за конкретной проблемы прямо сейчас. Медленная сборка — сколько минут в день суммарно по команде. Сложный для понимания модуль — сколько часов уходит на оценку задач в нём. Отсутствие тестов — сколько времени занимает ручная проверка перед каждым релизом. Переведите это в деньги через стоимость часа работы команды.</p><p><b>Шаг 2. Оцените объём работ.</b> Сколько займёт исправление — в часах, с буфером на непредвиденное. Это тоже деньги: стоимость часа умножить на объём.</p><p><b>Шаг 3. Посчитайте горизонт окупаемости.</b> Если текущие потери составляют 20 000 рублей в неделю, а объём работ стоит 80 000 рублей, рефакторинг окупается за четыре недели. Дальше каждая неделя — чистая экономия. Если горизонт окупаемости укладывается в срок, на который планируется развитие продукта, рефакторинг финансово обоснован.</p><p><b>Шаг 4. Введите два порога вместо полного ранжирования.</b> Определите верхний порог — задачи выше него идут в работу в приоритете над новыми фичами, потому что их текущие потери слишком высоки. И нижний порог — задачи ниже него откладываются, потому что их стоимость исправления не окупится в обозримом горизонте. Всё между порогами сравниваете через RICE: потенциальный эффект умножить на охват и коэффициент уверенности, разделить на трудоёмкость.</p><p>Оценки на втором и третьем шаге будут частично экспертными — это нормально. Главное, чтобы все задачи оценивались по одной шкале: тогда их можно сравнивать между собой, даже если абсолютные цифры приблизительны.</p><h2>Как обосновать рефакторинг бизнесу</h2><p>Если задачи по техдолгу регулярно появляются в бэклоге и приоритизируются наравне с фичами, отдельного разговора с бизнесом обычно не требуется. Но когда техдолг накопился до уровня, где нужно остановить разработку фич и заниматься только стабилизацией, этот разговор неизбежен.</p><p>Аргумент «там плохой код» не работает, потому что для бизнеса это не проблема — проблема это замедление или остановка поставки фич. Поэтому аргументы должны быть на том же языке.</p><ol><li>Скорость поставки. Покажите данные из трекера: сколько времени уходило на задачи в конкретном модуле полгода назад и сколько уходит сейчас. Если время выросло в полтора раза при сопоставимой сложности задач, это измеримое замедление с понятной причиной.</li><li>Предсказуемость оценок. Команда с накопленным техдолгом не может точно эстимировать: задача на три дня превращается в две недели, потому что в процессе обнаруживаются связанные проблемы. Для бизнеса это означает срыв сроков и невозможность планировать релизы. Покажите расхождение между оценками и фактом за последние несколько спринтов.</li><li>Риски. Неподдерживаемые зависимости, отсутствие покрытия тестами на критических участках, архитектура, которая не выдержит роста нагрузки при следующем масштабировании. Риски переводятся в деньги через вероятность и стоимость инцидента.</li></ol><p>Если легаси уже влияет на скорость разработки, поддержку или риски эксплуатации, полезно начинать не с переписывания, а с обследования системы. На этом этапе нужно понять, какие части кода, базы данных и инфраструктуры действительно требуют модернизации, где достаточно локального рефакторинга, а где проблема лежит в процессах или качестве проверки.</p><p>Когда легаси уже влияет на скорость разработки, поддержку и риски эксплуатации, задачу лучше начинать с обследования системы. Centicore Group занимается модернизацией таких систем: помогает разобраться с устаревшим кодом, базами данных и инфраструктурой, а затем спланировать изменения без хаотичного переписывания всего подряд.</p><h2>Итого</h2><p>Технический долг становится управляемым, когда у него есть не только описание, но и цена: что нужно исправить, какие проценты команда платит сейчас и как эти проценты будут расти, если продолжать разработку поверх проблемного участка.</p><p>По факту, это обычное инженерно-финансовое решение: либо команда закрывает объём долга сейчас, либо продолжает оплачивать его через более дорогие фичи, поддержку, регресс и риски в проде.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как приручить legacy-код: безопасная модернизация без заморозки фич</title>
      <link>https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki</link>
      <comments>https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[KODE]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki</guid>
      <description><![CDATA[<p>Как модернизировать legacy-код без остановки продукта: Strangler Fig Pattern, feature flags, shadow testing и безопасная миграция данных. Практика и антипаттерны.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-priruchit-legacy-kod-bezopasnaya-modernizaciya-bez-zamorozki">Как приручить legacy-код: безопасная модернизация без заморозки фич</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Ruby]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[MIT]]></category>
      <category><![CDATA[Финтех]]></category>
      <category><![CDATA[ICO]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Twitter]]></category>
      <category><![CDATA[Игра]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Kubernetes]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[аналитик]]></category>
      <category><![CDATA[OpenTelemetry]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[DeFi]]></category>
      <category><![CDATA[Grafana]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Legacy]]></category>
      <category><![CDATA[Техника]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 23 Jun 2026 06:16:57 GMT</pubDate>
      <content:encoded><![CDATA[<p>Legacy-код — одна из самых болезненных тем в инженерных командах. Обычно все понимают, что система устарела: архитектура мешает быстро выпускать изменения, новые фичи приходится встраивать через обходные пути, тесты либо неполные, либо отсутствуют, а любое изменение в одном модуле неожиданно ломает другой.</p><p>Но при этом к такому коду часто боятся прикасаться. И не без причины. В старых системах редко бывает понятная карта зависимостей. Документация устарела, часть знаний живет только в головах нескольких разработчиков, а бизнес при этом продолжает ждать новых релизов, интеграций и продуктовых экспериментов.</p><p>Так появляется классическая ловушка legacy: систему надо модернизировать, но остановить развитие нельзя. Переписать всё с нуля страшно, поддерживать как есть — всё дороже. В результате продукт обрастает временными решениями, скорость разработки падает, а стоимость каждого следующего изменения растет.</p><p>Хорошая новость в том, что модернизация legacy-кода не обязана быть большим взрывом. Старую систему можно менять постепенно, сохраняя рабочий продукт, не замораживая фичи и не устраивая один критический релиз, от которого зависит всё.</p><h2>Почему Big Bang-переписывание чаще всего заканчивается плохо</h2><p>Когда команда долго живет с устаревшей системой, идея переписать всё с нуля выглядит очень соблазнительно. Кажется, что можно наконец избавиться от технического долга, выбрать нормальную архитектуру, перепроектировать модули, покрыть всё тестами и начать «правильно».</p><p>На старте такой план часто звучит логично. Особенно если текущая система действительно мешает развитию. Например, мобильное приложение растет, у него уже миллионы пользователей, бэкенд написан несколько лет назад как монолит, а каждая новая фича требует изменений в десятке мест. Команда устала чинить регрессии, бизнес устал ждать, и всем хочется «один раз нормально переписать».</p><p>Проблема не в самой идее переписывания, а в условиях, при которых оно проваливается. Большой риск возникает, когда совпадают четыре фактора: переписывание занимает много месяцев, в это время бизнес продолжает развивать старую систему, новая версия покрывает сразу большую часть функциональности, а откат связан с миграцией данных. Если все четыре пункта присутствуют, Big Bang почти гарантированно превратится в долгий и дорогой проект.</p><p>Допустим, команда решила переписать модуль заказов в e-commerce-продукте. В старой версии есть корзина, промокоды, доставка и оплата. Команда планирует за полгода сделать новый сервис заказов. Но за эти полгода бизнес добавляет подписки, подарочные сертификаты, частичную оплату бонусами и новую логику возвратов. В итоге новая система, которую проектировали под старые требования, к моменту релиза уже нуждается в доработке.</p><p>Есть и другая проблема: большой релиз почти всегда несет максимальный риск. Если вы заменяете крупный кусок системы целиком, ошибка влияет сразу на большую часть пользователей. Откат тоже становится сложным, потому что новая логика уже связана с новыми данными, контрактами и интеграциями.</p><p>Big Bang всё-таки бывает оправдан — но в узких условиях. Если кодовая база молодая (год-два), пользователей мало, у системы нет критичного состояния в БД и продукт можно временно заморозить или вести в обоих контурах параллельно, полное переписывание может оказаться дешевле постепенной миграции. Это редкая ситуация, и она быстро исчезает по мере роста продукта. В зрелых системах безопаснее работает другой подход — постепенная архитектурная эволюция.</p><h2>Пример: как команда переписала сервис документов и потеряла полгода</h2><p>Команда сопровождала сервис — старый модуль на aiohttp с Pydantic v1, через который проходила вся обработка путевых листов и актов осмотра транспорта. Сервис существовал шесть лет, был покрыт тестами фрагментарно, а его API использовали мобильное приложение водителей, диспетчерская веб-панель и пакетный импорт.</p><p>Команда решила переписать сервис целиком: перейти на FastAPI, обновить Pydantic до v2, заодно почистить контракты и заменить внутреннее хранилище документов с MongoDB на PostgreSQL. План был рассчитан на четыре месяца.</p><p>Через восемь месяцев проект всё ещё не был готов к выкатке, а к десятому месяцу команда откатила миграцию полностью. Причин было несколько.</p><p>Во-первых, переписывание шло параллельно с продуктовой разработкой. За время миграции бизнес добавил два новых типа документов и изменил правила подписи актов. Новая система проектировалась под старые требования и к моменту готовности уже не соответствовала продукту.</p><p>Во-вторых, команда не написала характеристических тестов. Поведение «как есть» нигде не было зафиксировано, и расхождения находились только в продакшене после переключения.</p><p>В-третьих, у старого сервиса были скрытые побочные эффекты, о которых никто не помнил. При смене статуса документа публиковал событие в Kafka, которое читал биллинг и сервис аналитики. В новой реализации это поведение не было воспроизведено, потому что в коде оно выглядело как «лишний» вызов. После переключения биллинг перестал получать события, и расхождение обнаружили только через две недели — по жалобе финансового отдела.</p><p>В-четвёртых, переключение было сделано «в лоб»: маршрут в API Gateway просто перенаправили на новый сервис. Фича-флага не было, теневого запуска не было, плана отката не было. Когда выяснилось, что новый сервис строже валидирует исторические форматы документов и отклоняет часть старых записей, быстро вернуться на старую реализацию не получилось — её к тому моменту уже отключили на стенде, а в БД успели уйти записи в новом формате.</p><p>В итоге миграцию свернули, потратив около десяти человеко-месяцев и потеряв доверие бизнеса. Сервис до сих пор работает в исходной реализации, а команда переходит к плану, описанному ниже.</p><h2>Strangler Fig Pattern: как заменить систему по частям</h2><p>Один из самых практичных подходов к модернизации legacy-кода — Strangler Fig Pattern. В софтверном виде паттерн был сформулирован Мартином Фаулером в 2004 году под названием StranglerFigApplication. Идея проста: не переписывать систему целиком, а постепенно выносить отдельные части в новую реализацию.</p><p>Название пришло из биологии. Фикус-душитель растет вокруг дерева-хозяина и постепенно вытесняет его. В архитектуре принцип похожий: старая система продолжает работать, новая функциональность появляется рядом, а затем отдельные потоки постепенно переводятся на новую реализацию.</p><p>Представим старый монолит интернет-магазина. Внутри него есть каталог, корзина, заказы, платежи, скидки, личный кабинет и уведомления. Переписать всё сразу — рискованно. Но можно начать с относительно изолированного участка, например с уведомлений.</p><p>Сначала команда описывает текущий контракт: какие события приходят в модуль уведомлений, какие каналы используются, какие шаблоны отправляются, какие ошибки считаются допустимыми. Затем рядом создается новый сервис уведомлений, который реализует тот же контракт. На первом этапе он может даже не отправлять реальные сообщения, а только принимать события и логировать результат. После проверки часть трафика переводится на новую реализацию. Когда сервис стабилизируется, старый код уведомлений удаляется из монолита.</p><p>Strangler Fig хорошо работает там, где между старым и новым кодом есть сетевая граница: HTTP, message bus, RPC. Если такой границы нет — например, нужно постепенно заменить функцию или класс внутри одного процесса — используется родственный паттерн Branch by Abstraction: над старой реализацией создается абстракция, рядом пишется новая реализация, переключение происходит через конфигурацию или фича-флаг, после стабилизации старая ветка удаляется. Снаружи это выглядит как Strangler Fig, но без сетевого прокси.</p><p>Такой подход снижает риск. В системе нет одного большого релиза, где всё меняется сразу. Есть серия небольших контролируемых изменений. Каждое можно протестировать, измерить и откатить.</p><h2>Главное правило: сначала повторить поведение, потом улучшать</h2><p>Одна из частых ошибок при модернизации legacy-кода — попытка одновременно переписать систему и улучшить бизнес-логику. Команда смотрит на старый модуль и думает: «Раз уж мы его трогаем, давайте сразу сделаем нормальную архитектуру, изменим контракты, уберем странные кейсы и перепишем поведение».</p><p>Это опасный путь. В legacy-системах странное поведение часто существует не случайно. За ним может стоять неочевидное бизнес-правило, старый клиент, интеграция с внешней системой или исторический баг, на который уже кто-то завязался.</p><p>Например, в системе расчета налогов может быть правило: для контрактов, заключенных до 2018 года, НДС округляется в меньшую сторону до целого рубля, а для всех остальных — по математическим правилам. Новый разработчик может решить, что это ошибка, и «исправить» округление. Но потом выяснится, что часть крупных клиентов держит это поведение в своих сверках, а смена правила приведет к расхождениям в актах и претензиям.</p><p>Прежде чем менять поведение, его нужно зафиксировать. Для этого пишут характеристические тесты (characterization tests, иногда называемые golden master или approval tests). Идея простая: на реальных данных или их обезличенных копиях прогоняется старая реализация, её ответы сохраняются как эталон, и любые будущие изменения, отклоняющиеся от эталона, отлавливаются автоматически. Тесты пишутся не для красоты, а для того, чтобы зафиксировать существующее поведение — даже странное — перед тем, как его трогать. Подробно эта техника описана у Майкла Физерса в книге Working Effectively with Legacy Code; на практике её удобно реализовать через библиотеки семейства approval-tests (approvaltests-python, approvaltests-java и аналоги).</p><p>Поэтому первый этап модернизации — не улучшение, а воспроизведение текущего поведения. Новая реализация должна вести себя так же, как старая. Даже если старое поведение кажется странным. Только после стабилизации можно отдельно обсуждать, что именно стоит менять.</p><h2>Feature toggles: как включать новую логику без риска</h2><p>Feature toggles, или фича-флаги, — один из главных инструментов безопасной миграции. Они позволяют включать и выключать новую логику без деплоя.</p><p>В обычной разработке релиз часто выглядит бинарно: код либо выкатили, либо нет. При миграции legacy это неудобно. Гораздо безопаснее иметь возможность включить новую реализацию для 1% пользователей, затем для 10%, потом для половины аудитории и только после этого для всех.</p><p>Например, команда переносит расчет стоимости доставки из монолита в новый сервис. С помощью фича-флага это выглядит так:</p><p>user_id передается явно, чтобы решение «попал ли пользователь в новый сегмент» было стабильным от запроса к запросу. Иначе один и тот же клиент будет получать разные ответы при обновлении страницы, и поведение системы станет непредсказуемым.</p><p>На первом этапе флаг включают только для внутренней команды. Потом для тестового сегмента пользователей. Затем для небольшой доли реального трафика. Если метрики стабильны, долю увеличивают. Если появляются ошибки, флаг выключают, и пользователи снова идут в старую реализацию.</p><p>Важно различать два разных типа флагов. Флаг постепенной выкатки (rollout flag) меняется редко и контролирует, какой процент пользователей видит новую логику. Kill switch — отдельный флаг, единственная задача которого — мгновенно выключить новую реализацию при инциденте. Kill switch должен опрашиваться на каждом запросе, его кэширование должно жить секунды, а не минуты, и он принципиально не должен зависеть от той системы, которую он выключает. Иначе в момент аварии может оказаться, что выключатель сам недоступен.</p><p>В качестве инфраструктуры для флагов команды обычно берут одну из платформ: LaunchDarkly, Unleash, Flagsmith, GrowthBook, либо собирают собственную поверх Redis или конфигурационного сервиса. Для миграции важны три свойства: быстрое распространение изменений (секунды, а не минуты), поддержка таргетинга по пользователю/сегменту и аудит — кто и когда менял флаг.</p><p>Важно, что фича-флаг — это не просто if в коде. Для серьезной миграции нужны правила: кто может включать флаг, как быстро его можно отключить, какие метрики отслеживаются, когда флаг должен быть удален.</p><p>Последний пункт особенно важен. Если флаги не удалять, система быстро превращается в набор ветвлений, где никто уже не понимает, какая логика актуальна.</p><h2>Shadow testing: как проверить новую систему на реальном трафике</h2><p>Feature toggles помогают безопасно переключать пользователей. Но перед этим хорошо бы понять, совпадает ли новая логика со старой. Для этого используют shadow testing.</p><p>Shadow testing — это запуск новой реализации параллельно старой, но без влияния на пользователя. Пользовательский запрос по-прежнему обрабатывает старая система, а новая получает копию запроса и считает результат «в тени». Пользователю этот результат не показывается. Команда только сравнивает ответы.</p><p>Например, есть старый модуль расчета скидок. Он учитывает промокоды, сегмент пользователя, историю покупок, регион и партнерские условия. Команда пишет новый сервис скидок. Чтобы не переключать пользователей сразу, можно запустить теневой режим:</p><p>Два момента, на которые стоит обратить внимание в этом коде. Теневой вызов запускается через asyncio.create_task — корутина сразу планируется в event loop и начнёт выполняться, как только функция вернёт управление. И весь блок завернут в try/except: исключение в новой логике не должно ронять основной запрос. Без этих двух свойств shadow testing рискует ухудшить продакшен вместо того, чтобы безопасно его проверить.</p><p>Небольшая оговорка для продакшена: event loop держит на task только слабую ссылку, и без сохранённой ссылки задача может быть собрана сборщиком мусора прямо во время выполнения. В реальном коде Task имеет смысл класть в set фоновых задач и удалять оттуда через add_done_callback. В примере выше эта обвязка опущена для читаемости.</p><p>Для критичной доменной логики — платежей, биллинга, расчета тарифов — допустимый уровень расхождения должен быть около нуля: цель в shadow-режиме не «как можно меньше различий», а «понимаем каждое расхождение». Для менее чувствительных доменов (рекомендации, ранжирование результатов поиска) можно жить с расхождением в долях процента, но и там расхождения нужно классифицировать, а не игнорировать. Возможно, это баги новой реализации. А возможно, старая система содержит устаревшую логику, которую нужно отдельно обсудить с бизнесом.</p><p>Shadow testing особенно полезен для критичных доменных частей: платежей, биллинга, расчета тарифов, персональных предложений, транзакций. Там нельзя просто «попробовать на пользователях» и посмотреть, что будет.</p><p>При этом важно отличать теневую проверку чтения от теневой проверки записи. Чтение проверить относительно дёшево: запрос идёт в обе системы, ответы сравниваются, никаких внешних эффектов нет. С записью всё сложнее. Если новая реализация в shadow-режиме действительно создаст заказ, спишет деньги или отправит письмо, у пользователя возникнут двойные эффекты. Поэтому для writes либо вводят идемпотентные ключи и shadow-режим без реальных побочных действий (внешние вызовы заменены no-op-стабами, БД — отдельной shadow-копией), либо вообще отказываются от теневой проверки записи в пользу постепенной выкатки за фича-флагом.</p><p>Сравнение ответов в реальной системе тоже не сводится к одной функции compare. Нужно отдельно решать, как игнорировать «нормальный» шум (метки времени, идентификаторы, порядок коллекций), как сэмплировать трафик, чтобы не утопить хранилище расхождений, и как организовать триаж — кто и в каком ритме разбирает накопившиеся диффы. Готовые решения этого класса — GitHub Scientist (Ruby и его порты в другие языки), Twitter Diffy, либо собственный лёгкий регистратор поверх Kafka и таблицы расхождений.</p><h2>С чего начинать модернизацию</h2><p>Начинать лучше не с самого больного и не с самого центрального модуля. Это звучит контринтуитивно, потому что обычно хочется сразу взяться за главный источник проблем. Но если начать с ядра системы, команда быстро упрется в максимальное количество зависимостей и рисков.</p><p>Удобный способ выбрать первый кусок — оценить кандидатов по двум осям: насколько модуль критичен для бизнеса (low / high) и насколько сильно он связан с остальной системой (low / high). Начинать стоит с квадранта low-criticality + low-coupling: ошибки в нем не уронят бизнес-показатели, а малое количество зависимостей позволит провести миграцию полностью, не утянув за собой смежные модули. Высоко-критичные и сильно связанные части (платежи, ядро авторизации) трогают в последнюю очередь — на этот момент команда уже наберёт опыт безопасной миграции.</p><p>Хорошие точки входа обычно: уведомления, генерация отчетов, поиск, история операций, профиль пользователя, отдельная часть каталога. Важно, чтобы у команды была возможность описать контракт: какие данные входят, какие выходят, какие ошибки возможны, какие внешние системы участвуют.</p><p>Допустим, в банковском приложении есть старый модуль истории операций. Он медленный, сложно расширяется, но при этом не выполняет сами транзакции. Это хороший кандидат для первой миграции. Ошибка в истории операций неприятна, но обычно менее критична, чем ошибка в списании денег.</p><p>Команда может вынести чтение истории в отдельный сервис, сначала запустить его в shadow-режиме, потом включить для части пользователей, затем полностью перевести чтение на новую реализацию. При этом критичная транзакционная логика останется в старой системе до тех пор, пока команда не наберет опыт безопасной миграции.</p><h2>Миграция данных: самая сложная часть</h2><p>Большая часть статьи говорит о маршрутизации запросов и переключении трафика. Но в реальных проектах основная сложность лежит ниже — в данных. Старая и новая реализации почти всегда работают с общим состоянием: одной БД, одним хранилищем документов, одним набором очередей. Переехать туда «одним коммитом» нельзя.</p><p>Базовый рабочий приём — Expand-Contract (он же Parallel Change). Изменение схемы делается в три такта. На этапе expand в БД добавляются новые поля, таблицы или индексы, при этом старое поведение полностью сохраняется. Затем — migrate: обе реализации начинают писать и в старое, и в новое место (dual writes), а отдельный фоновый процесс делает backfill — заполняет новые поля историческими данными. После этого читатели по одному переключаются на новую схему. Только когда никто из читателей не использует старую структуру, наступает contract — удаление лишних колонок и таблиц.</p><p>Несколько практических деталей, которые часто упускают:</p><p>·         Dual writes — это не бесплатная операция. Две записи означают две точки отказа. Если одна из них упала, нужно решать, что делать: продолжать ли работу, ставить ли событие в очередь на повтор, помечать ли запись как несогласованную. Простое «сначала пишем туда, потом сюда» в продакшене на нагрузке приводит к расхождениям.</p><p>·         Backfill часто длиннее, чем кажется. На большой таблице миграция в одном UPDATE блокирует продакшен. Поэтому backfill делают батчами по N тысяч строк с паузами, отслеживают прогресс и предусматривают возможность остановить и продолжить.</p><p>·         Онлайн-изменения схемы на крупных таблицах делаются не штатным ALTER TABLE, а специализированными инструментами: gh-ost или pt-online-schema-change для MySQL, встроенные онлайн-механизмы PostgreSQL для индексов и колонок, Liquibase/Flyway — для управления версионированием изменений в репозитории.</p><p>·         Shadow testing данные не покрывает. Можно сравнить, что новая реализация возвращает то же, что и старая, но если за этим стоит другая схема в БД, проверка корректности самой миграции данных — это отдельная работа: сверки, контрольные суммы, выборочный аудит исторических записей.</p><p>Без этих шагов любая красивая фасадная архитектура наталкивается на разъезжающиеся данные — и тогда даже идеальный Strangler Fig снаружи не спасает.</p><h2>Прокси-слой как точка контроля</h2><p>Чтобы постепенно заменять legacy-код, нужно управлять маршрутизацией запросов. Для этого часто создают прокси-слой, API Gateway или фасад, через который проходит обращение к старой и новой логике. В терминах Domain-Driven Design такой слой часто называют Anti-Corruption Layer: он защищает новую реализацию от старых контрактов и наоборот, позволяя двум моделям сосуществовать без взаимного «загрязнения».</p><p>Без такой точки контроля миграция становится хаотичной. Часть клиентов ходит напрямую в старый модуль, часть — в новый, часть использует обходные пути, а команда теряет возможность централизованно переключать трафик.</p><p>Прокси-слой решает несколько задач. Он скрывает детали реализации от клиентов, позволяет направлять часть запросов в новую систему, поддерживает фича-флаги, собирает метрики и упрощает откат.</p><p>В качестве технической основы команды обычно берут один из трех вариантов: классический API gateway (Kong, AWS API Gateway), service mesh (Envoy, Istio) или более простой reverse proxy (NGINX, HAProxy). Service mesh особенно удобен, когда трафик уже идёт внутри Kubernetes-кластера: маршрутизацию можно менять конфигурацией, без правок кода клиентов и сервисов.</p><p>Например, мобильное приложение обращается к endpoint /orders/history. Раньше этот endpoint напрямую обслуживал монолит. После введения API Gateway приложение продолжает ходить по тому же контракту, но внутри gateway может решать, куда направить запрос: в legacy-модуль или новый сервис истории заказов.</p><p>Управление маршрутизацией обычно делается не «всё или ничего», а на основании атрибутов запроса: значения заголовка (X-Migration-Cohort: new), куки, хэша от user-id (стабильное разбиение пользователей на сегменты) или географического региона. Это позволяет выкатывать новую реализацию сначала на одну страну, на сотрудников самой компании или на тестовый сегмент — и только потом расширять охват.</p><p>Для клиента ничего не меняется. Для команды появляется управляемость.</p><h2>Наблюдаемость: без метрик миграция превращается в гадание</h2><p>Постепенная модернизация невозможна без нормальной наблюдаемости. Если команда не видит, что происходит внутри системы, она не сможет безопасно переключать трафик.</p><p>Минимальный набор — это логи, метрики и распределенная трассировка (distributed tracing). Нужно понимать, сколько запросов идет в старую и новую реализацию, сколько ошибок возникает, как меняется latency, где появляются таймауты, какие статусы возвращаются, какие бизнес-метрики проседают.</p><p>Технические метрики стоит формулировать не как «средний ответ» и «процент ошибок», а в терминах SLI и SLO: целевые показатели вида «99.9% запросов на /orders/history отвечают быстрее 300 ms за 30 дней» с явным error budget. Latency измеряется по перцентилям (p50, p95, p99) — среднее значение почти всегда обманчиво, а хвосты распределения говорят о реальном опыте пользователя. На время миграции имеет смысл выставить отдельные SLO для нового и старого пути и сравнивать их.</p><p>В качестве инструментов де-факто стандартом стал OpenTelemetry для трассировок, метрик и логов — единый протокол, который пишет в практически любое хранилище. Дальше — Prometheus и Grafana для метрик, Jaeger или Tempo для traces, Sentry или аналог для ошибок. Для миграции важна возможность фильтровать метрики по «варианту» — отдельно по старому и новому пути — иначе все цифры смешаются и реальную динамику будет не видно.</p><p>Технических метрик недостаточно. Если команда переносит оформление заказа, важно смотреть не только на 500 ошибки и время ответа, но и на конверсию в оплату, количество брошенных корзин, повторы запросов, обращения в поддержку.</p><p>Пример: новая система формально отвечает быстрее старой и не дает ошибок. Но после включения на 10% пользователей падает конверсия в оплату. Причина может быть не в серверной ошибке, а в изменении порядка полей, другом тексте сообщения или потере какого-то edge-case. Без бизнес-метрик команда может решить, что миграция успешна, хотя для продукта она уже создает проблему.</p><h2>Практическая последовательность миграции</h2><p>Рабочая последовательность обычно выглядит так.</p><p>Сначала команда выбирает ограниченный участок системы. На этом этапе важно не просто назвать модуль, а описать его границы. Какие сценарии он закрывает? Кто его вызывает? Какие данные он читает и пишет? Какие внешние интеграции использует? Какие неочевидные бизнес-правила в нем есть?</p><p>Затем поверх legacy-логики создается стабильный контракт. Это может быть API, фасад, gateway или отдельный слой внутри приложения. Главная задача — сделать так, чтобы клиенты зависели не от внутренней реализации, а от понятного интерфейса. На этом этапе полезно вспомнить про contract testing (Pact, Spring Cloud Contract): автотесты со стороны потребителей фиксируют, что именно они ожидают от API, и предупреждают о ломающих изменениях до того, как они доедут до продакшена.</p><p>После этого рядом пишется новая реализация. Она должна повторять текущее поведение, а не сразу становиться «идеальной версией будущего». На этом этапе полезно фиксировать все расхождения: где старая система работает странно, где требования не описаны, где бизнес-правила требуют уточнения.</p><p>Следующий этап — shadow testing. Новая система получает копии реальных запросов, считает результат, но пользователю по-прежнему возвращается ответ legacy. Команда сравнивает результаты и устраняет расхождения.</p><p>Когда новая реализация достаточно стабильна, начинается постепенное переключение через feature toggles. Сначала внутренние пользователи, потом 1% реального трафика, затем 5–10%, затем 50% и только после этого 100%.</p><p>На каждом этапе команда смотрит на метрики. Если всё стабильно, движение продолжается. Если появляются проблемы, флаг выключается, трафик возвращается в legacy, а команда разбирает причины.</p><p>Последний этап — удаление старого кода. Это не формальность, а обязательная часть миграции. И «удалить старый код» — это не один коммит, а явный Definition of Done: вырезана старая ветка кода, удалён фича-флаг, обновлена документация и схемы архитектуры, переименованы или удалены устаревшие дашборды и алерты, обновлены runbook’и для on-call и проведено короткое внутреннее обучение. Если этого не сделать, через полгода никто уже не вспомнит, какой путь актуален, и легаси-ветвление останется в коде навсегда.</p><h2>Откат миграций: дешёвый только пока не пошли записи</h2><p>Откатить миграцию, в которой ещё не было записи в БД, легко: достаточно переключить фича-флаг, и трафик снова идёт через старую реализацию. Откатить миграцию, в которой новая система уже неделю писала данные в новые таблицы, — отдельный, гораздо более тяжёлый разговор.</p><p>Поэтому ещё на этапе проектирования каждое изменение должно сопровождаться явным планом отката. Удобно различать три типа шагов.</p><p>Полностью обратимые шаги. Чтение через новый сервис, расчёт «в тени», новые метрики. Откат — выключить флаг. Это самый комфортный режим, и в нём стоит держать миграцию как можно дольше.</p><p>Обратимые с компенсацией. Новая реализация пишет дополнительные данные (например, дублирует операции в новую таблицу), но старый источник тоже обновляется. Откат возможен, но требует решить, что делать с уже записанными данными: оставить, очистить, синхронизировать. План этих действий должен быть написан до выкатки, не во время инцидента.</p><p>Forward-only. После некоторой точки откат становится невозможен — например, после того, как старая схема удалена или внешние интеграции перенастроены на новый сервис. Такие шаги допустимы, но к ним нужно приходить отдельно, осознанно, с особенно строгими SLO в предыдущем этапе. До forward-only-перехода имеет смысл подержать систему в режиме параллельной работы дольше, чем по графику.</p><p>Базовое правило: ни один шаг миграции не должен уходить в продакшен, если у команды нет письменного ответа на вопрос «как мы откатываемся в случае проблемы». Иначе при инциденте откатываться будут на ходу — и не факт, что успешно.</p><h2>Пример: как тот же сервис мигрировали со второй попытки</h2><p>После неудачного опыта команда взялась за тот же сервис заново, но изменила подход.</p><p>На первом шаге они зафиксировали поведение существующего сервиса. На самые часто используемые сценарии (создание путевого листа, подпись акта осмотра, выгрузка пакета документов за период) написали характеристические тесты на реальных продакшен-данных, обезличенных и сохранённых как фикстуры. Любое будущее изменение поведения теперь падало в CI как явное расхождение.</p><p>Параллельно команда провела инвентаризацию побочных эффектов. Из исходного кода и логов выяснилось, что сервис не только хранит документы, но и: публикует событие в Kafka при смене статуса, инкрементирует счётчик в Redis для рейтинга водителей, отправляет webhook во внешнюю систему партнёра, пишет в таблицу аудита. Каждый из этих эффектов попал в отдельный пункт чек-листа «что должно остаться» в новой реализации.</p><p>Затем команда выбрала первый кусок для выноса — не весь сервис, а только чтение документов (GET /documents/{id} и GET /documents/by-driver/{driver_id}). Это была наименее рискованная часть: ошибки в чтении неприятны, но не ломают финансовые потоки.</p><p>Новый сервис написали на FastAPI рядом со старым. На уровне API Gateway появилось правило маршрутизации: запросы на чтение шли в старый сервис, но в фоне дублировались в новый. Ответ пользователю всегда возвращал legacy, а ответ нового сервиса сравнивался с эталоном и записывался в отдельную таблицу для разбора. Использовали обёртку поверх asyncio.create_task — на ответ пользователя теневой вызов не влиял.</p><p>За три недели shadow-режима команда нашла четыре расхождения. Два оказались багами новой реализации (округление времени, неправильная сортировка вложений). Два — давно забытыми особенностями старого сервиса (одно поле возвращалось в UTC, другое — в локальной зоне; так было исторически, бизнес не возражал, но в новой реализации захотели единый формат). Все четыре зафиксировали явно: баги — починили, особенности — согласовали с продуктовой командой как осознанное изменение.</p><p>Когда расхождений не осталось, включили фича-флаг на сотрудников самой компании. Через неделю — на 1% реальных водителей. Дальше шаг по 5%, 25%, 50%, 100% с паузой в несколько дней между этапами. На каждом шаге следили не только за HTTP-ошибками и latency, но и за продуктовыми метриками: количество подписанных актов, время от открытия документа до подписи, доля повторных запросов. Один раз пришлось откатиться с 25% на 5% — в одном из регионов выросло время отклика из-за неэффективного запроса. Исправили, выкатили снова.</p><p>Через два месяца чтение полностью перешло в новый сервис. Старый код чтения и фича-флаг удалили в том же релизе. После этого по той же схеме мигрировали запись документов, потом публикацию событий, потом импорт из внешних систем. Полная миграция заняла девять месяцев — почти столько же, сколько провалившийся Big Bang, — но продукт всё это время продолжал развиваться, инцидентов не было, и в конце команда осталась с системой, которую понимает.</p><h2>Типичные ошибки при работе с legacy</h2><p>Первая ошибка — пытаться улучшить всё сразу. Команда одновременно меняет архитектуру, бизнес-логику, контракты и инфраструктуру. В результате становится невозможно понять, какая именно часть вызвала проблему. Правильнее сначала воспроизвести поведение, стабилизировать новую реализацию и только потом улучшать.</p><p>Вторая ошибка — недооценивать скрытые зависимости и побочные эффекты. Legacy-код часто делает больше, чем кажется. На один и тот же вызов могут быть навешаны: запись в таблицу аудита, инкремент счётчика в кэше, публикация события в очередь, обновление статуса связанной сущности, инвалидация кэша, дёрганье webhook’а во внешнюю систему. Если в новой реализации воспроизвести только явный путь, скрытые потребители молча перестанут получать данные — и узнают об этом через жалобу бизнеса, а не через ошибку в логах. Поэтому перед выносом любого модуля имеет смысл составить инвентаризацию побочных эффектов: пройтись по коду и логам и выписать каждое нелогичное действие отдельным пунктом чек-листа.</p><p>Третья ошибка — отсутствие наблюдаемости. Без логов, метрик и трассировки команда не управляет миграцией, а угадывает. Особенно опасно смотреть только на технические ошибки и игнорировать бизнес-показатели.</p><p>Четвертая ошибка — не договариваться с бизнесом. Модернизация не должна быть невидимой «инженерной активностью в стол». Её нужно встраивать в roadmap, объяснять эффект и договариваться о приоритетах. Если бизнес не понимает, зачем команда тратит время на миграцию, работа будет постоянно проигрывать новым фичам.</p><p>Пятая ошибка — не удалять старый код. Временное сосуществование старой и новой логики нормально. Вечное сосуществование — нет. Если legacy не удаляется, технический долг не уменьшается, а просто меняет форму.</p><p>Шестая ошибка — не удалять фича-флаги после миграции. Флаг, который сыграл свою роль и больше никогда не выключается, превращается в постоянное ветвление в коде. Через год команда не помнит, можно ли удалить такую ветку или там сидит важный edge-case. Через два — кода с такими «мёртвыми» флагами становится больше, чем основной логики. Поэтому каждый флаг должен заводиться с условием удаления («после полной выкатки и двух недель стабильной работы») и иметь ответственного, кто этим удалением займётся.</p><p>Отдельно стоит упомянуть организационную сторону. Закон Конвея работает и в обратную сторону: если новый и старый код владеются разными командами с разными приоритетами, миграция будет тормозиться независимо от выбранного паттерна. На время миграции имеет смысл явно проговорить, кто отвечает за переход, и не разделять старую и новую реализации между несовместимыми roadmap’ами.</p><h2>Компромиссы, к которым нужно быть готовыми</h2><p>Постепенная модернизация безопаснее Big Bang-переписывания, но она не бесплатна. Некоторое время система будет сложнее, чем раньше. В ней появятся старый и новый код, прокси-слой, фича-флаги, дублирование логики, дополнительные метрики.</p><p>Shadow testing увеличит нагрузку на инфраструктуру, потому что часть запросов будет обрабатываться дважды. Команде придется поддерживать дисциплину: документировать контракты, отслеживать флаги, удалять старую реализацию после миграции, поддерживать contract-тесты в актуальном состоянии.</p><p>Но это контролируемая сложность. Она распределена во времени и управляется инженерными практиками. В отличие от Big Bang-риска, где команда долго работает с минимальной обратной связью, а потом выкатывает один большой релиз с максимальной неопределенностью.</p><h2>Когда Strangler Fig особенно оправдан</h2><p>Постепенная миграция особенно хорошо подходит для систем, где downtime невозможен или слишком дорог. Это финтех, e-commerce, биллинг, мобильные бэкенды с большой аудиторией, высоконагруженные продукты, старые монолиты и системы с большим количеством интеграций.</p><p>Если продуктом ежедневно пользуются сотни тысяч или миллионы людей, нельзя позволить себе «переписать и посмотреть, что будет». Нужно менять архитектуру так, чтобы пользователь не замечал процесса миграции.</p><p>Этот подход также полезен там, где бизнес продолжает активно развивать продукт. Если фичи нельзя заморозить на полгода, модернизация должна идти параллельно с продуктовой разработкой.</p><h2>Когда модернизацию лучше не делать</h2><p>Постепенная миграция — мощный инструмент, но у неё тоже есть стоимость, и иногда правильный ответ — оставить систему как есть. Несколько сценариев, в которых модернизация плохо окупается.</p><p>Продукт, который уходит из эксплуатации. Если через год сервис будет выключен или заменён на покупное решение, тратить квартал на его рефакторинг бессмысленно. Достаточно стабилизировать то, что есть.</p><p>Модуль, который никто не трогает. Если код десятилетней давности продолжает работать, не падает, не требует изменений и не вызывает инцидентов, его «уродливость» — не повод его переписывать. Цель модернизации — упростить будущие изменения; если будущих изменений нет, цели тоже нет.</p><p>Регулируемые системы с тяжёлой ресертификацией. В банковских, медицинских и государственных контурах любое изменение в критичной системе может потребовать повторной сертификации, перепрохождения аудитов, обновления договорной обвязки. В таких условиях стоимость модернизации может на порядок превышать стоимость поддержки текущей реализации, и решение нужно принимать вместе с владельцем продукта и юристами, а не только инженерным составом.</p><p>Простой тест: если на вопрос «какой бизнес-сценарий мы откроем после миграции» нет внятного ответа — модернизацию имеет смысл отложить и заняться чем-то другим.</p><h2>Что получает команда</h2><p>Главный результат постепенной модернизации — управляемость. Команда начинает лучше понимать систему, контролировать изменения и снижать риск инцидентов.</p><p>Появляются понятные контракты, наблюдаемость, практика безопасных релизов, культура удаления старого кода. Разработчики перестают бояться legacy, потому что у них появляется метод, а не только желание «когда-нибудь всё переписать».</p><p>Для бизнеса это тоже выгодно. Продукт продолжает развиваться, сроки становятся более прогнозируемыми, риски крупных сбоев снижаются, а технический долг постепенно уменьшается.</p><h2>Модернизация — это процесс, а не проект</h2><p>Legacy нельзя «починить за квартал». Если система развивалась годами, она не станет простой после одного рефакторинга. Но её можно системно улучшать.</p><p>Strangler Fig Pattern, Branch by Abstraction, feature toggles, shadow testing и аккуратная миграция данных дают рабочую модель: выбрать ограниченный участок, описать контракт, реализовать новую версию, проверить её на реальном трафике, постепенно переключить пользователей и удалить старый код.</p><p>Это не самый быстрый путь. Зато он управляемый. А в зрелых продуктах управляемость важнее скорости.</p><p>Потому что цель модернизации — не написать красивую новую систему. Цель — сделать так, чтобы продукт продолжал развиваться, команда могла безопасно вносить изменения, а пользователи не становились участниками инженерного эксперимента.</p>]]></content:encoded>
    </item>
    <item>
      <title>Скрытый сбой идемпотентности в финтех-системе: разбор инцидента</title>
      <link>https://tproger.ru/articles/skrytyj-sboj-idempotentnosti-v-finteh-sisteme-razbor-incidenta</link>
      <comments>https://tproger.ru/articles/skrytyj-sboj-idempotentnosti-v-finteh-sisteme-razbor-incidenta?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дмитрий Москалюк]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/skrytyj-sboj-idempotentnosti-v-finteh-sisteme-razbor-incidenta</guid>
      <description><![CDATA[<p>Разбор реального production-инцидента в финтех-системе: почему ошибка HTTP 500 не остановила операцию создания карты и как сбой идемпотентности в API Gateway вызвал массовые дубликаты. Практический кейс о микросервисной архитектуре, distributed systems, request-id, API idempotency и техническом долге.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/skrytyj-sboj-idempotentnosti-v-finteh-sisteme-razbor-incidenta">Скрытый сбой идемпотентности в финтех-системе: разбор инцидента</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[Микросервисы]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Финтех]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[микро]]></category>
      <category><![CDATA[faq]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 18 May 2026 04:55:17 GMT</pubDate>
      <content:encoded><![CDATA[<h2>Как все началось</h2><p>Всё началось с безобидного, почти рутинного тикета в саппорт:</p><p><i>«У меня тут несколько одинаковых карт создалось. Я вроде один раз нажимал, а их штук пять висит в приложении». </i></p><p>Для финтеха подобная фраза — не мелкая UI-аномалия, а сигнал тревоги высшего уровня. Когда речь идёт о платёжных инструментах, дубль сущности мгновенно переходит из разряда «странностей» в категорию «полноценный инцидент».</p><p>Поначалу казалось, что это просто один случай. Но на самом деле, проблема уже какое-то время была, хоть и скрытно: люди получали дубликаты своих банковских карт, но массово на это никто не жаловался. На уровне первой линии поддержки это ошибочно классифицировали как пользовательские ошибки</p><p>Настоящий шум поднялся не внутри компании, а уже в соцсетях. Один из клиентов выложил пост. Там была фотография посылки, а в ней — примерно сотня одинаковых карт. Пост очень быстро стал популярным, и вот тогда-то и возникли проблемы с репутацией, а команда разработчиков только тогда и узнала о случившемся. И особенно тревожно это звучало на фоне того, что мы считали что таких проблем быть не может - ведь считалось, что у  нас глобальная идемпотентность на все запросы.</p><p>Мы начали разбираться. Стандартный мониторинг показывал норму: дашборды зелёные, в логах тихо, метрики CPU и памяти без отклонений. При этом в базе обнаруживались дубликаты, которых быть не должно. И только тогда, когда мы внимательно посмотрели на коммунальный API Gateway, то поняли что одно изменение от другой команды поменяло идемпотентность работы endpoint-а.</p><p>Проблема была структурно невидима для тех, кто находился ближе всего к ней. Отсутствие видимой проблемы не равно отсутствию риска — система просто ждала подходящего момента.</p><h2>Что произошло: хронология инцидента</h2><p>Архитектура была классической: Клиент → API Gateway → микросервисы,.</p><p>Сценарий развивался почти незаметно для стандартных средств наблюдения:</p><p>1. Фронтенд: Пользователь нажимает кнопку «Выпустить карту».</p><p>2. Gateway: Запрос начинает обрабатываться в API Gateway.</p><p>3. Микросервис по созданию карт: честно выполняет работу: создаёт запись в БД и возвращает `200 OK` в Gateway.</p><p>4. Новый микросервис: API Gateway вызывает новый микросервис и получает HTTP 500 в ответ от него. Исключение возникает уже после успешного вызова нашего микросервиса, и это ключевая точка разрыва: Gateway считает весь запрос неудавшимся, а ответ нашего микросервиса теряется.</p><p>5. Клиент: получает HTTP 500</p><p>6. Реакция: Пользователь видит красную плашку ошибки и логично решает: «Не сработало, пробую снова». Более того, с точки зрения протокола HTTP - запросы, в ответ на которые пришла ошибка HTTP 500, можно пытаться отправлять опять.</p><p>7. Петля: Микросервис, ничего не зная о судьбе предыдущих ответов, послушно создавал карту за картой.</p><p>Круг замкнулся. Эпидемия началась.</p><p>В компании такого уровня это не было предусмотрено. И это такая обидная ошибка.</p><h2>Как так получилось?</h2><p>Вскрылось ошибочное предположение:</p><p><i>«В API Gateway вызов микросервиса создания карт всегда идет последним». </i></p><p>Таким образом идемпотентность достигалась «формально» - ведь если создание карты завершалось с ошибкой - это точно означало что и вызов API Gateway тоже завершится с ошибкой. Сработало ложное чувство безопасности.</p><p>Ответ на вопрос “почему так было сделано?” очень простой - это было осознанное упрощение на старте. Все знали об этом, но задача на фикс потерялась в недрах бэклога на очень долгое время. Это классический пример того, как архитектурное допущение и отложенный рефакторинг годами живут в проде, пока их не вскрывает редкая последовательность отказов.-</p><h2>Что потребовалось изменить</h2><p>Пришлось в срочном порядке внедрять полноценный механизм идемпотентности по request-id, который не зависел бы от порядка вызова downstream-сервисов. И это было достаточно тяжело, потому что окно для легкого внедрения закрывается в первый день продакшена: до этого момента нет ни живых пользователей, ни накопленных данных, ни клиентов старых версий, с которыми нужно сохранять обратную совместимость. Ниже — ответы на вопросы, которые мы получили от коллег, когда разбирали этот инцидент.</p><h2>FAQ: Часто задаваемые вопросы</h2><p><b>1. Почему нельзя просто заблокировать кнопку на фронте?</b></p><p>Блокировка кнопки решает проблему только при стабильной сети. Если запрос ушёл, сервер создал сущность, но ответ потерялся — кнопка разблокируется по таймауту, и пользователь нажмёт снова. Это не устраняет корневую причину, а лишь слегка снижает вероятность дубля.</p><p><b>2. Чем идемпотентность отличается от дедупликации в БД?</b></p><p>Дедупликация через уникальные индексы защищает от дублей в хранилище, но не решает проблему сайд-эффектов: повторный запрос всё равно вызовет отправку SMS, печать банковской карты, генерацию событий в шине или списание средств. Идемпотентность гарантирует, что вся цепочка выполнится ровно один раз — включая все внешние вызовы и побочные действия.</p><p><b>3. Как долго хранить ключи идемпотентности?</b></p><p>На практике мы хранили ключи в основной базе данных без ограничения срока — затраты на хранение UUID по всем сущностям оказались небольшими. В общем случае минимальный срок зависит от конкретных сценариев использования — кому-то хватит и  часа, а кому-то нужна неделя. В любом случае, окна должно быть достаточно, чтобы покрыть сценарии, когда пользователь возвращается к повтору запроса, например, на следующий день или когда клиентское приложение автоматически перезапускает отложенные запросы после восстановления сети. Бессрочное хранение не обязательно, но слишком короткий TTL создаёт риск дублей при длительных сетевых проблемах.</p><p><b>4. Что делать со старыми клиентами, которые не шлют request_id?</b></p><p>Мы сделали несколько версий API для создания карт — под разные версии приложения. Для новых клиентов работала полноценная идемпотентность с клиентским ключом. Для старых версий приходилось принимать риски и генерировать request_id на стороне сервера. Альтернативой может быть хэширование payload запроса, но это менее надежно и сложнее: таймстемпы и случайные поля могут отличаться от вызова к вызову. Поэтому мы выбрали  подход с генерацией ключа на сервере для устаревших клиентов: риски дублей на переходный период оказались меньше, чем сложность поддержки двух схем валидации одновременно.</p><p><b>5. Обязательно ли делать идемпотентность для всех методов API?</b></p><p>Идемпотентность требуется только для методов, которые изменяют состояние системы — POST, PUT, PATCH и иногда DELETE. Методы чтения (GET, HEAD, OPTIONS) не изменяют данные, поэтому считаются идемпотентными по умолчанию.</p><p><b>6. Как понять, что в вашей системе уже есть скрытая проблема с дублями?</b></p><p>Лучший способ — ввести метрики превентивно, не дожидаясь жалоб пользователей. Отслеживайте количество повторных вызовов с одинаковым ключом идемпотентности и сравнивайте его с общим числом запросов. Если метрики уже показывают ненулевое значение — проблема есть, даже если внешне всё работает незаметно.</p><p>Если метрик ещё нет, вот три косвенных признака, которые помогут заподозрить неладное:</p><ul><li>В базе данных периодически появляются записи с одинаковым содержимым, созданные с разницей в несколько секунд.</li><li>Пользователи жалуются на дубликаты карт, заказов или платежей, но вы не можете воспроизвести проблему локально, списываете на то, что пользователи что-то делают не так</li><li>В логах API Gateway периодически всплывают HTTP 500 ошибки, но downstream-сервисы при этом отрабатывают успешно.</li></ul><p>Если заметили хотя бы один из этих симптомов — простого решения уже не будет. Однако остаётся возможность исправить ситуацию до того, как проблему заметят пользователи. В нашем случае дубли проявлялись редко и стали массовыми только при повышении нагрузки. Если отложить решение, скрытая проблема перейдет на уровень, где её последствия станут заметны снаружи и потребуют значительно больших усилий.</p><h2>Итог</h2><p>Главный урок, который мы вынесли: идемпотентность — это общая ответственность всех команд разработки, и поломать её может быть проще, чем кажется. А добавить в уже работающую систему быстро и дёшево — почти невозможно.</p><p>Метрик на всплески повторных запросов у нас не было. А зря — это самый дешёвый способ увидеть проблему до того, как она обрушит продакшен. Системы, спроектированные на 10% нагрузки, ломаются на 60% — и обычно это становится неожиданностью для команды.</p><h2>Практический чек-лист: что проверить в своей системе уже сегодня</h2><ul><li>Убедитесь, что все критические мутирующие эндпоинты поддерживают ключ идемпотентности.</li><li>Настройте алерты на аномальное количество запросов на создание сущностей от одного пользователя за короткий промежуток времени.</li><li>Проверьте, как ваш API Gateway обрабатывает ошибки — не теряет ли он ответы downstream-сервисов.</li><li>Убедитесь, что фронтенд корректно обрабатывает не только 200, но и 500, 502, 504, не провоцируя пользователя на повторные клики без необходимости.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Как подключить общую память к Claude Code и Cursor за 5 минут</title>
      <link>https://tproger.ru/articles/kak-podklyuchit-obshhuyu-pamyat-k-claude-code-i-cursor-za-5-minut</link>
      <comments>https://tproger.ru/articles/kak-podklyuchit-obshhuyu-pamyat-k-claude-code-i-cursor-za-5-minut?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[C0Ally]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-podklyuchit-obshhuyu-pamyat-k-claude-code-i-cursor-za-5-minut</guid>
      <description><![CDATA[<p>Как подключить общую память к Claude Code и Cursor за 5 минут, общая shared память для ИИ-агентов. Общий контекст и экономия ресурсов. CoAlly</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-podklyuchit-obshhuyu-pamyat-k-claude-code-i-cursor-za-5-minut">Как подключить общую память к Claude Code и Cursor за 5 минут</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Опрос]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Slack]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Навыки]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 09 May 2026 07:40:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Работаю с Claude Code и Cursor параллельно. Claude Code хорош для архитектурных задач и рефакторинга, Cursor - для быстрого редактирования и code review. Проблема одна: они друг про друга ничего не знают. И даже при выполнении задач в одной среде разработки через пару дней уже и не вспомнишь что решил, почему, приходится опять просить агента изучить контекст и разобраться чтобы получить нужную информацию. Это время и токены.</p><p>Сделал себе инструмент, который это решает. Потом оформил в продукт. Называется <b>CoAlly</b> - сервер, который дает агентам общую память.</p><h2>Что он делает</h2><p>Агент автоматически сохраняет контекст работы: какие решения принял, какие файлы затронул, почему сделал так, а не иначе. Когда другой агент (или тот же, но в новой сессии) берется за связанную задачу, он находит этот контекст через семантический поиск.</p><p>Запрос "проблемы с авторизацией" найдет запись про "token refresh rotation", хотя слова совершенно разные. Работает через эмбеддинги и косинусное расстояние, скоро выйдет фича с анализом смысловой корреляции при поиске.</p><h2>Подключение</h2><h2>Шаг 1: регистрация</h2><p>Заходим на <a href="https://coally.cortexally.ai">coally.cortexally.ai</a>, создаем аккаунт. Получаем API ключ.</p><h2>Шаг 2: подключение к агенту (Claude, Cursor и др.)</h2><p>Одна команда в терминале:</p><p>claude mcp add coally-nexus --transport sse https://coally.cortexally.ai/sse --header "Authorization: Bearer ВАШ_КЛЮЧ"</p><p>Перезапускаем Claude Code. В списке MCP-инструментов должен появиться coally-nexus.</p><p><b>Cursor, Windsurf, VSCode</b></p><p>Заходим в настройки Cursor -&gt; Settings -&gt; MCP -&gt; Settings (значок шестеренки) или создаем/редактируем файл `.cursor/mcp.json` в корне проекта (или в домашней директории для глобального подключения):</p><p>Файл `~/.codeium/windsurf/mcp_config.json` для Windsurf или соответствующее меню для VSCode и других его форков.</p><p>Перезапускаем IDE|Agent. Готово.</p><h2>Первая сессия</h2><p>После подключения агент при первом взаимодействии проиндексирует проект автоматически. Если нет - попросите: "<i>проиндексируй этот проект через CoAlly</i>".</p><p>Что происходит при индексации: сканируется дерево проекта, находятся конфиги, важные воспоминания, memorybanks и др., все это эмбеддится и сохраняется как контекст.</p><p>Дальше агент сам начнет сохранять контекст работы после значимых изменений. Если нужно явно сохранить что-то: "<i>сохрани контекст этой задачи в CoAlly</i>".</p><h2>Что получаем</h2><p>На вопрос "<i>что вчера делали по задаче авторизации? какой статус бага с токеном и сколько времени потребуется на доработку</i>" Агент достает из <b>CoAlly</b> записи: "<i>вчера в Cursor поправили ротацию токенов, причина была в TTL, затронули два файла. Могу сразу продолжить и на основе коммитов ... исправить точечно флоу, время на исправление — N минут.</i>"</p><p>Если работаете в команде - еще полезнее. Контекст коллеги тоже доступен. Его экспертиза и навыки также постепенно приобретают цифровую версию. Не нужно ждать стендап или писать в Slack.</p><p><b>Ограничения</b></p><p>Агенты иногда игнорируют инструкции и не сохраняют контекст. Бывает. Можно просить явно.</p><p>Семантический поиск хорош, но не идеален. Если запрос слишком общий ("что нового?"), результаты будут размытыми. Конкретные вопросы работают лучше.</p><p>Бесплатный тариф - 2 проекта. Для личного использования хватает. Для команд - тарифы Team и Enterprise.</p>]]></content:encoded>
    </item>
    <item>
      <title>Маск: «ИИ будет создавать бинарники напрямую» — программирование якобы уйдет в прошлое</title>
      <link>https://tproger.ru/news/mask---ii-budet-sozdavat-binarniki-napryamuyu----programmirovanie</link>
      <comments>https://tproger.ru/news/mask---ii-budet-sozdavat-binarniki-napryamuyu----programmirovanie?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/mask---ii-budet-sozdavat-binarniki-napryamuyu----programmirovanie</guid>
      <description><![CDATA[<p>Маск заявил, что ИИ скоро будет создавать бинарники напрямую, но эксперты сомневаются в отказе от исходного кода и компиляторов</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/mask---ii-budet-sozdavat-binarniki-napryamuyu----programmirovanie">Маск: «ИИ будет создавать бинарники напрямую» — программирование якобы уйдет в прошлое</a>»</p>]]></description>
      <category><![CDATA[Компиляторы]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Илон Маск]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 17 Feb 2026 02:17:09 GMT</pubDate>
      <content:encoded><![CDATA[<p>Илон Маск <a href="https://timesofindia.indiatimes.com/technology/tech-news/elon-musk-gives-less-than-a-year-to-coding-as-a-profession-says-there-is-no/articleshow/128244238.cms" rel="nofollow">заявил</a> на встрече с сотрудниками xAI, что уже к концу 2026 года «никто не будет писать код». Якобы ИИ сможет создавать бинарные файлы напрямую.</p><p>По его словам, нейросеть сможет делать это даже эффективнее традиционных компиляторов.</p><p>Звучит громко. Но если разобрать тезис технически, все не так однозначно.</p><h2>Что именно предлагает Маск</h2><p>По словам миллиардера, вместо привычной цепочки «исходный код → компилятор → бинарник», ИИ будет получать задачу и сразу выдавать исполняемый файл. Без промежуточного слоя в виде читаемого кода.</p><p>Фактически это означает отказ от исходников как основного артефакта разработки. Промпт на входе, а .exe или ELF на выходе.</p><h2>Почему это спорно с технической точки зрения</h2><p>Компиляторы — это детерминированные системы. Они проверяют синтаксис и типы, строят промежуточное представление программы, применяют оптимизации и гарантируют воспроизводимый результат.</p><p>Один и тот же код при одинаковых условиях дает один и тот же бинарник.</p><p>LLM работают иначе: они генерируют вероятностный результат. Малейшая ошибка в бинарнике приведет к падению программы или трудноуловимому багу.</p><p>В отличие от исходного кода, бинарный файл невозможно нормально ревьюить, сравнивать в Git или проверять на уровне логики.</p><p>Кроме того, компиляция стоит дешево — это миллисекунды CPU. Генерация крупных бинарников через LLM потребовала бы миллионов токенов и значительно больших вычислительных затрат.</p><h2>Где ИИ действительно меняет разработку</h2><p>На фоне громких заявлений Маска, многие как будто забыли, что ИИ уже активно используется в программировании. Но в другом формате. Технология помогает писать исходный код, объяснять сложные участки, генерировать тесты, предлагать рефакторинг.</p><p>А дальше в дело все равно вступает компилятор — как проверяемый и предсказуемый этап.</p><p>Более реалистичный сценарий — улучшение компиляторов с помощью ИИ, а не их замена. Например, автоматический подбор оптимизаций или более агрессивная векторизация.</p>]]></content:encoded>
    </item>
    <item>
      <title>JetBrains закрыла ИИ IDE Fleet. Вместо него выйдет новый продукт для ИИ-агентов</title>
      <link>https://tproger.ru/news/jetbrains-zakryla-ii-ide-fleet--vmesto-nego-vyjdet-novyj-produkt-dlya-ii-agentov</link>
      <comments>https://tproger.ru/news/jetbrains-zakryla-ii-ide-fleet--vmesto-nego-vyjdet-novyj-produkt-dlya-ii-agentov?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/jetbrains-zakryla-ii-ide-fleet--vmesto-nego-vyjdet-novyj-produkt-dlya-ii-agentov</guid>
      <description><![CDATA[<p>JetBrains закрыла IDE Fleet и прекращает поддержку проекта. Вместо редактора компания готовит новый продукт для ИИ-агентной разработки</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/jetbrains-zakryla-ii-ide-fleet--vmesto-nego-vyjdet-novyj-produkt-dlya-ii-agentov">JetBrains закрыла ИИ IDE Fleet. Вместо него выйдет новый продукт для ИИ-агентов</a>»</p>]]></description>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[JetBrains]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 16 Dec 2025 14:25:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>JetBrains <a href="https://blog.jetbrains.com/fleet/2025/12/the-future-of-fleet/">объявила</a> о закрытии <b>IDE Fleet</b>, над которой компания работала несколько лет.</p><p>Начиная <b>с 22 декабря 2025 года</b>, ее больше нельзя будет скачать. Ни через Toolbox App, ни через другие официальные каналы. Разработка и выпуск обновлений также прекращаются.</p><p>При этом JetBrains подчеркивает: речь идет не о провале эксперимента, а о смене стратегии. Команда Fleet, технологическая база и наработки никуда не исчезают — вместо IDE компания готовит <b>новый продукт, ориентированный на агентную разработку</b>.</p><h2>Почему Fleet не взлетела как IDE</h2><p>Fleet задумывалась как <b>попытка переосмыслить IDE JetBrains</b> через более легкую архитектуру, современный UI и отказ от наследия IntelliJ Platform.</p><p>С технической точки зрения, <b>эксперимент оказался успешным</b> — многие компоненты Fleet уже используются в других IDE компании. При этом отдельные UX-решения и вовсе были переняты всей линейкой продуктов.</p><p>Однако <b>как самостоятельный продукт, Fleet не смогла занять четкую нишу</b>. Она не смогла заменить IntelliJ IDEA и другие IDE JetBrains, но и не предложила достаточно убедительных преимуществ, чтобы существовать параллельно с ними.</p><p>Пользователи постоянно спрашивали, <b>какую IDE выбирать</b>.  А наличие двух схожих линеек только усиливало путаницу.</p><h2>Ставка на ИИ тоже не спасла Fleet</h2><p>JetBrains пыталась перепозиционировать Fleet как <b>AI-first редактор</b>. Команда экспериментировала с новыми сценариями и проводила масштабные пользовательские исследования.</p><p>Вывод оказался неприятным, но честным: <b>еще один ИИ-редактор не имеет шансов выделиться на фоне десятков ИИ-ориентированных форков VSCode</b>.</p><p>В результате JetBrains решила сосредоточить развитие ИИ-функций внутри уже существующих IDE, где они действительно приносят ценность.</p><h2>Новый фокус — агентная разработка</h2><p>Параллельно с работой над Feet, компания заметила формирование нового подхода к программированию. <b>Разработчики все чаще делегируют задачи ИИ-агентам, а не пишут код вручную</b>.</p><p>Речь идет о рефакторинге, обновлении тестов, исследовании незнакомого кода и даже реализации фич целиком — с последующим ревью результата.</p><p>Такой воркфлоу плохо сочетается с классической моделью IDE, где все строится вокруг синхронной работы и мгновенной обратной связи.</p><p>Поэтому JetBrains и решила не «втискивать» агентные сценарии в IDE, а создать отдельную среду разработки для ИИ-агентов. Именно в этом виде Fleet получила вторую жизнь — но уже под новым именем и с другим позиционированием.</p><h2>Что будет с текущими пользователями Fleet</h2><p>Если Fleet уже установлена, ею можно продолжать пользоваться. Однако обновлений больше не будет. А функции, завязанные на серверные сервисы JetBrains — <b>включая AI Assistant</b> — со временем могут перестать работать.</p><p>JetBrains обещает отдельно рассказывать о новом продукте по мере его разработки.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как мигрировать на Node.js 22 без рисков: инструкция</title>
      <link>https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya</link>
      <comments>https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Светлана Гринь]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya</guid>
      <description><![CDATA[<p>Node.js 22 стал надёжным стандартом для компаний, которым важно сохранить стабильность после завершения поддержки прежних версий. Разберём практические шаги по безопасной миграции. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-migrirovat-na-node-js-22-bez-riskov--instrukciya">Как мигрировать на Node.js 22 без рисков: инструкция</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Инфраструктура]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 14 Dec 2025 12:40:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Node.js 22 сегодня становится дефолтным выбором для компаний, которые откладывали обновление годами. Вокруг этой версии сформировалась зрелая экосистема, а крупные команды уже успели протестировать релиз в продакшене. Разберём, что думают разработчики о платформе и какие подводные камни вас ждут при миграции.</p><h2>Почему релиз Node.js 22 важен</h2><p>30 апреля 2025 года завершился жизненный цикл версии Node.js 18. Сообщество заранее предупреждало об этом через официальный аккаунт в X. Там напомнили разработчикам о необходимости перейти на более свежие версии — Node.js 20 или 22. Восемнадцатая версия перестала получать патчи безопасности, поэтому обновление стало критичным для поддержания стабильной работы приложений.</p><p>Наиболее надёжным вариантом стала миграция на Node.js 22, которая сейчас находится в статусе долгосрочной поддержки (LTS) и гарантирует обновления безопасности и стабильность на ближайшие годы.</p><p>Хотя релиз этой версии не выглядел революционным, он существенно изменил основу платформы. Многие возможности, которые раньше требовали внешних библиотек, теперь встроены прямо в платформу: WebSocket-клиент, стабильный fetch(), работа с файлами, Blob и URL, улучшенная совместимость ESM и CommonJS. Это уменьшает количество зависимостей, снижает вероятность уязвимостей и значительно упрощает поддержку проектов.</p><p>Помимо удобства разработки, в новой версии усилились производительность и безопасность. Обновлённый движок V8 ускоряет выполнение кода, а оптимизация старта и снижение потребления памяти делают Node.js более стабильным в высоконагруженных и serverless-сценариях. Улучшенная модель разрешений, строгие настройки TLS и расширенная поддержка AbortController повышают защиту на уровне рантайма, ограничивая последствия уязвимостей в сторонних пакетах. Это даёт компаниям возможность строить более надёжные сервисы с меньшими затратами на контроль рисков и эксплуатацию.</p><p>Node.js 22 также стал удобнее для работы гибридных команд. Стабильная интеграция ESM и CommonJS облегчает миграцию старых проектов, а унификация Web-API делает код универсальным и понятным даже для сотрудников без глубокого опыта в бэкенде. Улучшенные инструменты тестирования и диагностики повышают прозрачность разработки и снижают время на нахождение ошибок.</p><blockquote>Node.js 22 — эволюционный релиз. Он развивает ранее введённые возможности (fetch, ESM, WebSocket и др.), но не переворачивает парадигму разработки. Это укрепление уже заданного курса.</blockquote><h2>Возможности Node.js 22</h2><p><b>Расширение JavaScript и обновление V8.</b> Получив движок V8 12.x, Node.js подтянул все улучшения ECMAScript 2024. Код стал исполняться быстрее как за счёт оптимизаций в компиляторе, так и благодаря улучшенной работе с асинхронностью. Например, async/await теперь обрабатываются эффективнее, а создание структур данных, активно используемых в серверах, стало менее затратным.</p><p>С точки зрения разработчика изменения могут быть неочевидны, но приложения на реальной нагрузке выполняются быстрее, стабильнее и предсказуемее.</p><p><b>Corepack включён по умолчанию.</b> До выхода Node.js 22 разработчики сами решали, подключать ли Corepack. Теперь он активен без дополнительной настройки, и это серьёзное изменение в экосистеме. Все менеджеры пакетов контролируются единой прослойкой, что устраняет зависимость от версии, установленной глобально у разработчика или в CI.</p><p>На практике это означает, что версия менеджера пакетов становится частью проекта и перестаёт зависеть от среды. Ошибки вида «у меня работает, а у коллеги нет» заметно сокращаются. Благодаря этому процессы разработки и сборки становятся стабильнее.</p><blockquote>Corepack, как мне кажется, сильно упростит жизнь большим командам. Исчезает вечная история “а у меня Yarn другой версии” или “Pnpm не подтянулся”. Corepack берёт на себя управление версионированием менеджеров пакетов, а значит, сборки становятся более воспроизводимыми. Для корпоративных проектов это плюс, особенно если речь идёт о распределённых командах или CI/CD-конвейерах.</blockquote><p><b>WebSocket API и улучшенные Web Streams.</b> Node.js 22 внедрил нативную поддержку WebSocket API. Теперь можно создавать WebSocket-клиенты и серверы без сторонних библиотек вроде ws. Это делает код чище и ближе к браузерному API.</p><p>Параллельно были улучшены Web Streams. Работа с потоками стала предсказуемой, а инструменты вроде CompressionStream и DecompressionStream функционируют так же, как и в современных браузерах. Поддержка универсального кода — сервер + клиент — стала проще, а объём внешних зависимостей уменьшился.</p><p><b>Улучшения в работе CommonJS и ESM.</b> Node.js постепенно движется в сторону ESM, но у огромного количества проектов всё ещё используется CommonJS. В новой версии улучшена производительность смешанных проектов и сокращены ошибки при импорте модулей между двумя системами. Между ними появилось меньше «подводных камней», а значит миграция на ESM может идти постепенно, без ломки архитектуры.</p><p><b>Стабильный встроенный fetch().</b> Его реализация теперь полностью совпадает с браузерной, так что в простых кейсах можно отказаться от axios или node-fetch. Меньше зависимостей — быстрее запуск и меньше технического долга.</p><p><b>Развитие модели разрешений</b>, которая позволяет ограничивать доступ приложения к файловой системе, сети или переменным окружения прямо на уровне рантайма. Даже если зависимость уязвима, код не сможет выйти за пределы разрешённых прав. Это огромный шаг в сторону реальной безопасности.</p><blockquote>Я бы отметил более зрелую работу с Permission Model. Она уже не экспериментальная и реально помогает ограничивать доступ к файловой системе и сети. Параллельно улучшилась производительность V8, а вместе с ней и общая отзывчивость серверных приложений. Подтянули и Web-стек: стабильнее стали Web Streams, Request/Response и другие API, которые сближают Node с браузерами. Всё это не революция, но тренд на унификацию растёт.</blockquote><p><b>Расширенные возможности тестирования.</b> Node.js продолжает развивать встроенный тестовый фреймворк node:test. Теперь доступны кастомные репортеры, покрытие кода без внешних инструментов и mock timers (замена реальной работы таймеров). Это позволяет использовать встроенный тест-раннер для большинства задач, отказавшись от Jest или Vitest в простых проектах.</p><p><b>Обновления в безопасности.</b> Обновление OpenSSL — одно из самых чувствительных изменений в релизе. Поддержка старых криптоалгоритмов исключена, проверка сертификатов стала строже, а взаимодействие по TLS 1.3  стабильнее.</p><blockquote>С точки зрения безопасности рывка не произошло, но заметное движение есть: Permission Model, улучшенная работа с OpenSSL, более аккуратная валидация входящих данных. Всё это снижает риски, но полностью опасность не снимает, потому что реальная безопасность всё равно лежит в архитектуре, процессов CI/CD и культуре разработки.</blockquote><p><b>Повышенная производительность.</b> Заметные улучшения коснулись старта приложения, работы с памятью и производительности под нагрузкой. Приложения запускаются быстрее, а распределение нагрузки между worker threads стало более эффективным. Это позволит обслуживать больше запросов при тех же ресурсах и уменьшить расходы на инфраструктуру.</p><p><b>Международные стандарты и локализация.</b> Были обновлены Intl API на базе новой версии ICU. Преимущества: точные часовые пояса, поддержка новых календарей и систем чисел. Это критично для глобальных SaaS-платформ, аналитических сервисов и финтех-продуктов.</p><p>По словам Даниила Гоника, для разработчиков наиболее значимы такие изменения, как WebSocket-клиент, модульная система., обновление V8, улучшение JIT-компиляторов, добавление новых возможностей JS и улучшение встроенного test-раннера.</p><h2>Как безопасно мигрировать на Node.js 22</h2><p>Переход лучше начинать с <b>аудита и подготовки окружения</b>. Сначала стоит убедиться, что все зависимости поддерживают Node.js 22. Особенно это касается библиотек, которые используют нативные модули, криптографию и WebSockets. Проверка совместимости позволит избежать неожиданностей вроде ошибок компиляции или падений при запуске.</p><p>После этого нужно прогнать <b>тесты</b>. Даже минимальный набор позволит выявить проблемы, связанные с изменениями в ESM/CJS, WebSocket API или OpenSSL.</p><p>Само <b>обновление среды</b> зависит от выбранного инструмента. Через nvm или n процесс занимает секунды, а для Docker достаточно указать новый базовый образ. Если проект использует CI/CD, стоит уделить внимание Corepack: теперь он активен всегда, что может изменить поведение сборки.</p><p>После обновления полезно <b>проверить логи</b> и внимательно <b>изучить предупреждения</b>. Большинство проблем решается установкой последних версий библиотек, иногда — правкой конфигурации TLS или обновлением менеджера пакетов.</p><p>Обновление стоит внедрять поэтапно, начиная со staging или частичного продакшена, и держать готовность к откату.</p><blockquote>Я обычно советую подход двухконтурного обновления. Сначала поднять проект на 22-ю версию в отдельной среде, включить максимальный объём логов, запустить тесты и прогнать реальные сценарии. Потом обновить линтеры, сборщики и самые старые пакеты, чтобы убрать накопленные за годы предупреждения. И только когда всё стабильно, имеет смысл переключать продакшен. По сути, обновление Node нужно делать так же аккуратно, как обновление облачных сервисов: через изоляцию, наблюдаемость и бэкап-стратегию.</blockquote><blockquote>В Node.js 22 удалены некоторые устаревшие, малоиспользуемые функции, поэтому при переходе может потребоваться рефакторинг мест, где они использовались. Следует проверить депрекейты: Node.js 22 помечает ряд старых возможностей как устаревшие и предлагает альтернативы. Необходимо убедиться, что все библиотеки и пакеты, используемые проектом, поддерживают новую версию Node.js.<br />При скачке сразу с 18/20 на 22 некоторые зависимости могут оказаться несовместимыми с обновлённым движком V8 или новыми APIs — может понадобиться их обновление до последних версий.</blockquote><h2>Кому стоит подождать с переходом</h2><p>Переход на Node.js 22 подходит не всем проектам. В некоторых ситуациях разумнее подождать, чтобы избежать регрессий и поломок в продакшене. Вот кому стоит повременить с переходом:</p><ul><li>Тем, кто использует инструменты сборки или менеджеры пакетов, которые ещё не совместимы с Node.js 22 и могут выдавать ошибки или не работать вовсе.​</li><li>Командам, которые работают на старых версиях ОС. Известны проблемы совместимости с macOS 10.14 и ниже, а также возможны ошибки на Windows при миграции старых приложений.​</li><li>Тем, чьи CI/CD, контейнеры или Docker-образцы завязаны на конкретные версии Node.js и npm. Переход на 22-ю версию требует много регрессионного тестирования.​</li><li>Проектам с критически важными C++-добавками. Смена major-версии влечёт за собой обновление бинарных интерфейсов, что может вызывать ошибки при компиляции нативных модулей.</li><li>Пользователям определённых интеграций. Для некоторых версий MongoDB‑драйверов зафиксированы фатальные баги после перехода на конкретные промежуточные версии Node.js 22.x.​</li><li>Если вы используете проекты, которые сами рекомендуют Node.js 18 или 20, а обновление их зависимостей под 22 только планируется.</li></ul><blockquote>В продакшене чаще всего проблемы возникают не из-за самих изменений в платформе, а из-за того, как эти изменения проявляются в реальных проектах. Где-то обновился V8 и стал вести себя строже, где-то повылезали deprecated-модули, а где-то просто сломались сборщики или старые зависимости. Многие команды до сих пор живут на Webpack 4, Express 4 или TypeORM старых версий: вот они первыми столкнутся со сложностями.</blockquote><h2>Что дальше</h2><p>Среди будущих трендов в экосистеме <a href="http://node.js/">Node.js</a> Максим Захаренко выделяет следующие: <i>«Первое — постепенное сближение Node с Web-стеком, чтобы код становился максимально универсальным. Второе — рост влияния Bun и Deno, которые будут подталкивать Node к более агрессивной оптимизации. И третье — всё более активная интеграция с AI-инструментами, особенно в DevTools и в цепочках генерации кода»</i>.</p><blockquote>Всё больше веб-API внедряются в Node.js (fetch, WebSocket, EventTarget). Этот тренд будет продолжаться. ESM становится стандартом, поддержка будет расширяться, CommonJS постепенно уйдёт в прошлое. Расширяется WASI и поддержка WASM-модулей. Node.js движется в сторону мульти-языковой среды. Модель прав доступа и другие sandbox-механизмы также продолжат развиваться. Кроме того, в версии Node.js 22.5.0 экспериментально добавлен встроенный модуль SQLite (через –experimental-sqlite). Это важное уточнение: в релизе 22.0.0 SQLite отсутствовал.</blockquote><p>Даниил Гоник хотел бы увидеть в будущих релизах стабильную и удобную модель разрешений, возможность require() для ESM без флагов, улучшения в DevX: поддержка TS «из коробки», больше инструментов в ядре, расширение SEA (Single Executable Apps) и встроенные механизмы верификации зависимостей (подписи, integrity).</p><h2>Выводы</h2><p>Node.js 22 делает приложения быстрее, безопаснее и совместимее с Web API, улучшает работу с памятью и снижает зависимость от глобальных инструментов. Хотя переход требует подготовки, он не представляет серьёзных рисков, если следовать базовой процедуре: проверить зависимости, прогнать тесты и обновить среду постепенно. Если инфраструктура современная, а стек устойчивый, переход на Node.js 22 принесёт ощутимые преимущества как в скорости работы, так и в удобстве разработки.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработке обещают смерть последние 30 лет, теперь из-за ИИ. Почему она все еще живее всех живых</title>
      <link>https://tproger.ru/news/razrabotke-obeshhayut-smert-poslednie-30-let--teper-iz-za-ii--pochemu-ona-vse-eshhe-zhivee-vseh-zhivyh</link>
      <comments>https://tproger.ru/news/razrabotke-obeshhayut-smert-poslednie-30-let--teper-iz-za-ii--pochemu-ona-vse-eshhe-zhivee-vseh-zhivyh?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/razrabotke-obeshhayut-smert-poslednie-30-let--teper-iz-za-ii--pochemu-ona-vse-eshhe-zhivee-vseh-zhivyh</guid>
      <description><![CDATA[<p>30 лет предсказывают смерть разработки, теперь из-за ИИ. Но каждая «революция» лишь меняет инструменты, а не отменяет потребность в инженерах</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/razrabotke-obeshhayut-smert-poslednie-30-let--teper-iz-za-ii--pochemu-ona-vse-eshhe-zhivee-vseh-zhivyh">Разработке обещают смерть последние 30 лет, теперь из-за ИИ. Почему она все еще живее всех живых</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Машинное обучение]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 02 Dec 2025 12:36:14 GMT</pubDate>
      <content:encoded><![CDATA[<p>История разработки — это череда повторяющихся <b>прогнозов о ее скорой гибели</b>.</p><p>В сети появился <a href="https://www.jasonscheirer.com/weblog/vignettes/">материал</a>, автор которого — программист с 20+ летним стажем — вспоминает, как еще в 90-е взрослые уверяли его, что <b>программирование обречено</b>.</p><p>Тогда активно утверждалось, что <b>ООП решит все проблемы раз и навсегда</b>: появятся библиотеки-«кирпичики», а бизнес просто будет собирать приложения как LEGO, вообще без инженеров.</p><p>Прошли десятилетия, а профессия не только не исчезла — <b>она стала одной из самых востребованных</b>. Каждый уровень абстракции, который должен был «убить» разработчиков, просто поднимал планку и открывал новые задачи.</p><h2>Мультимедиа, IDE и другие «концы света», которые не случились</h2><p>В начале 90-х индустрия переживала <b>«мультимедийную революцию»</b>: казалось, что любая программа должна уметь работать со звуком и видео, иначе останется в прошлом.</p><p>Но через несколько лет <b>это стало обыденностью</b> — просто очередным &lt;video /&gt; в HTML. Никто массово не разорился из-за отсутствия мультимедиа в продуктах.</p><p>В 2000-х на горизонте появился <b>новый «убийца профессии»</b> — умные IDE вроде IntelliJ. Автодополнение, рефакторинг, перенос классов, исправление ошибок до сборки — все это казалось магией, которая вот-вот заменит людей.</p><p>Но <b>инструменты лишь ускорили работу</b>, а <b>не забрали ее</b>. IDE прекрасно переставляет код, но не пишет новую логику.</p><h2>Автоматизация в реальности: она освобождает время, а не людей</h2><p>Автор приводит два примера: он автоматизировал работу контрактника, мигрировавшего базу MUMPS, и автоматизировал собственные задачи по обновлению сайтов, боясь потерять работу. В обоих случаях люди остались на своих местах — просто стали заниматься чем-то другим.</p><p>Вывод простой: <b>работы становится не меньше, а больше</b>. Автоматизация убирает рутину, но не отменяет необходимости решать новые проблемы.</p><h2>Новые ввения приходят и уходят, но разработка остается</h2><p>Интернет, Web 2.0, машинное обучение — каждая волна начиналась как революция и заканчивалась чем-то будничным. Мы привыкли к тому, что технологии, которые вчера казались чудом, сегодня воспринимаются как мелочь.</p><p><b>Текущая волна ИИ — не исключение</b>. Да, большие языковые модели меняют рабочие процессы. Да, они автоматизируют часть задач.</p><p>Но автор призвал быть честными с самими собой: такие сдвиги редко уничтожают профессию. Они скорее превращают ее в новую версию самой себя.</p><h2>Вместо финала: ИИ не убьет разработчиков, но сделает их работу другой</h2><p>Пугающие прогнозы звучат громко, но реальность — она сложнее. Пока одни обещают «конец эпохи программистов», другие продолжают писать код, решать задачи и адаптироваться к новым инструментам.</p><p>Как и последние 30 лет, разработка остается живой потому, что мир постоянно усложняется. А значит — всегда будет кому этот мир программировать.</p>]]></content:encoded>
    </item>
    <item>
      <title>Лишь в 1% случаев разработчики используют дебаг в VS Code. В 99% — console.log()</title>
      <link>https://tproger.ru/news/issledovanie--v-1--sluchaev-razrabotchiki-ispolzuyut-otladku-vs-code--v-99----console-log--</link>
      <comments>https://tproger.ru/news/issledovanie--v-1--sluchaev-razrabotchiki-ispolzuyut-otladku-vs-code--v-99----console-log--?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/issledovanie--v-1--sluchaev-razrabotchiki-ispolzuyut-otladku-vs-code--v-99----console-log--</guid>
      <description><![CDATA[<p>Исследование показало: 99% разработчиков отлаживают код через console.log, а встроенный дебаггер VS Code используют лишь 1% времени</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/issledovanie--v-1--sluchaev-razrabotchiki-ispolzuyut-otladku-vs-code--v-99----console-log--">Лишь в 1% случаев разработчики используют дебаг в VS Code. В 99% — console.log()</a>»</p>]]></description>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 24 Oct 2025 12:56:08 GMT</pubDate>
      <content:encoded><![CDATA[<p>Как <a href="https://floustate.com/blog/developers-spend-1-percent-time-vscode-debugger">показало</a> исследование платформы <b>FlouState</b>, разработчики почти не пользуются встроенным отладчиком в Visual Studio Code. Для этого эксперты проанализировали <b>11 805 сессий 68 программистов</b> за три месяца.</p><p>Среднее время активного использования встроенного дебаггера — <b>1,4% от общего времени кодинга</b>. Это примерно <b>13 минут в месяц</b>.</p><p>В то же время медианное значение и того меньше — всего <b>0 минут</b>. Это означает: <i>большинство разработчиков не открывали отладчик вообще</i>.</p><h2>Console.log правит миром</h2><p>Как выяснилось, в 75% случаев разработчики никогда не ставили брейкпоинты, предпочитая старый-добрый console.log().</p><p>Остальные использовали отладчик лишь эпизодически: 10% — менее одного процента времени, и только 15% — чаще, чем раз в месяц.</p><p>В среднем структура работы выглядела так:</p><ul><li>46,2% времени — написание кода, включая «отладку логами»;</li><li>28,7% — чтение и анализ чужого кода и стектрейсов;</li><li>23,7% — рефакторинг и удаление отладочных вставок;</li><li>1,4% — работа в интерфейсе отладчика VS Code.</li></ul><h2>Почему так происходит</h2><p>Исследователи называют феномен «психологией мгновенного отклика»:</p><p><i>Сonsole.log("here")</i> дает результат через 3 секунды. Настроить дебаггер — 10 минут. Мозг выбирает дофамин, а не дисциплину.</p><p>Среди других причин — привычка (<i>«мы изучали console.log с первого дня»</i>), кажущаяся сложность интерфейса и эффект «еще одной строчки лога». Это когда проще добавить еще один console.log, чем переключаться в режим пошаговой отладки.</p><h2>Что это значит</h2><p>Исследование не утверждает, что разработчики не отлаживают код — просто <b>делают они это вручную</b>. В среднем около 15–20% рабочего времени уходит на расстановку логов, чтение вывода и перезапуск приложения.</p><p>Авторы FlouState считают, что освоение встроенного дебаггера могло бы сократить этот цикл в разы. Но для большинства программистов console.log() остается самым надежным и быстрым инструментом.</p><p><i>«VS Code имеет отладчик мирового уровня. Мы просто им не пользуемся», — подытожил автор исследования.</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Вышедший накануне Claude Haiku 4.5 проиграл Sonnet 4.5 и GPT-5 в тесте на рефакторинг</title>
      <link>https://tproger.ru/news/vywedwij-nakanune-claude-haiku-4-5-proigral-sonnet-4-5-i-gpt-5-v-teste-na-refaktoring</link>
      <comments>https://tproger.ru/news/vywedwij-nakanune-claude-haiku-4-5-proigral-sonnet-4-5-i-gpt-5-v-teste-na-refaktoring?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/vywedwij-nakanune-claude-haiku-4-5-proigral-sonnet-4-5-i-gpt-5-v-teste-na-refaktoring</guid>
      <description><![CDATA[<p>Claude Haiku 4.5 сгенерировал больше всего кода, но занял лишь 7-е место в тесте на рефакторинг — уступив GPT-5 и Claude Sonnet 4.5</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/vywedwij-nakanune-claude-haiku-4-5-proigral-sonnet-4-5-i-gpt-5-v-teste-na-refaktoring">Вышедший накануне Claude Haiku 4.5 проиграл Sonnet 4.5 и GPT-5 в тесте на рефакторинг</a>»</p>]]></description>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 16 Oct 2025 07:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>При тестировании недавно выпущенной <b>Claude Haiku 4.5</b> на задаче реструктуризации WebSocket-клиента, модель показала парадокс. Она генерировала <b>гораздо больше кода</b>, но по качеству оказалась в самом низу рейтинга.</p><p>Испытание выполняли на одной и той же TypeScript-задачe (экспоненциальный бэкофф, управление состоянием, очередь сообщений), а выводы оценивал динамический судья — GPT-5.</p><p>Всего было пять критериев: качество, полнота, корректность, производительность и безопасность.</p><h2>Результаты и рейтинг</h2><p>Полный список восьми протестированных моделей (счет/токены):</p><ol><li><b>GPT-5</b> — 93.4 / 7919 токенов</li><li><b>Claude Sonnet 4.5</b> — 89.0 / 8425 токенов = <b>OpenAI o3</b> — 89.0 / 5191 токен</li><li><b>Gemini 2.5 Pro</b> — 86.6 / 2621 токен</li><li><b>GLM 4.6</b> — 84.4 / 3334 токена</li><li><b>Claude Opus 4.1</b> — 81.6 / 6052 токена</li><li><b>Claude Haiku 4.5</b> — 74.4 / 13 666 токенов</li><li><b>Grok 4</b> — 70.0 / 888 токенов</li></ol><p>Haiku сгенерировал <b>наибольшее количество токенов</b> (13 666) и занял лишь <b>7-е место</b> по итоговой оценке.</p><h2>Почему Haiku хуже — проблема переусложнения</h2><p>Разбор показал: Haiku стремилась покрыть все — большие слои логирования, множество абстракций, метрики, дублированные функции. В результате:</p><ul><li>качество кода: 60/100 (много дублирования и смешанных ответственностей),</li><li>корректность: 65/100 (повторяющиеся определения, потенциальные ошибки в боилерплейте),</li><li>полнота: 90/100 (много функционала реализовано).</li></ul><p>Итого: модель «переписала» задачу в толстом стиле — много текста, но низкая поддерживаемость и повышенный риск ошибок.</p><p>Sonnet 4.5, напротив, дала более компактный, корректный и читабельный код: 8425 токенов и 89 баллов.</p><h2>Что это значит на практике</h2><p>Тест демонстрирует важную мысль: <b>больше кода ≠ лучшее решение</b>. Избыточность увеличивает поверхность ошибок, делает рефакторинг дороже и снижает шансы на безопасный продакшен-деплой.</p><p>Для инженеров и команд это сигнал — оценивать ИИ-генерацию не по объему, а по компактности, ясности и корректности.</p><h2>Вывод</h2><p>Claude Haiku 4.5 оказался эффективен в покрытии функциональности, но не в написании качественного, поддерживаемого кода.</p><p>Порог «полезности» генерации — это не токены, а соотношение качества к объему. Тут напрашивается простая рекомендация: тестируйте модели на реальных задачах, смотрите на корректность и поддерживаемость, а не только на полноту фич.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как мы автоматизировали мутационное тестирование unit тестов на проекте в крупном банке с использованием Stryker.NET</title>
      <link>https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net</link>
      <comments>https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[asyncguru]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net</guid>
      <description><![CDATA[<p>Опыт автоматизации мутационного тестирования Unit-тестов в крупном банке с помощью Stryker.NET. Практический кейс по внедрению в CI/CD, настройке и интеграции в legacy-проект. Как мы нашли слабые места в тестах и повысили их надёжность, не замедляя процесс разработки.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-my-avtomatizirovali-mutacionnoe-testirovanie-unit-testov-na-proekte-gazprombanka-s-ispolzovaniem-stryker-net">Как мы автоматизировали мутационное тестирование unit тестов на проекте в крупном банке с использованием Stryker.NET</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[.NET]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 11 Oct 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Качественные модульные тесты помогают оптимизировать ресурсы команды разработки и увеличивают надежность создаваемого продукта. Оценить эффективность этих тестов можно с помощью автоматизированного инструмента для мутационного тестирования Stryker.NET.</p><p><i>Меня зовут Юрий Каган, я Software/System Architect в IT_ONE. Расскажу об опыте применения </i><i>Stryker.NET </i><i>на проекте в крупном банке. </i><i></i></p><h2>Качественные юнит-тесты —  основа надежного ПО</h2><p>Несмотря на то, что юнит-тесты —  только один из компонентов пирамиды тестирования ПО, они —  база для создания качественного кода:</p><p>– Благодаря проверке отдельных фрагментов кода можно <b>выявить дефекты на ранних стадиях</b>, не пропуская их на следующие этапы разработки. Известно, что чем раньше обнаружена ошибка, тем ниже стоимость её исправления.</p><p>– Модульные тесты обеспечивают <b>стабильность разработки</b>, служа некой страховкой: они предотвращают регрессии при изменениях и рефакторинге.</p><p>– Проведённые тесты служат документацией и <b>ускоряют внедрение нового функционала</b>. По ним можно понять изначальный замысел автора кода и упростить разработку.</p><p>Для оценки качества юнит-тестов разработчики обычно пользуются метрикой Code Coverage, отражающей процент покрытия тестами исходного кода. Такая информация может собираться различными способами в зависимости от типа используемого инструмента, но результат получается схожий: данные о том, какие конкретно фрагменты кода выполнялись во время тестов. О каких бы то ни было проверках речи не идет. То есть, по нашему опыту, технически очень легко можно написать тесты, которые будут давать 100% Code Coverage и при этом ничего не проверять. Это означает, что даже высокий процент покрытия кода тестами не гарантирует, что они разработаны качественно и поведение вашего кода надёжно зафиксировано. После них нельзя исключать скрытые ошибки.</p><p>Как следствие, постоянно осознавая риски некачественных тестов, разработчики могут начать бояться рефакторинга —  ведь любые изменения потенциально грозят дефектами в коде. Причем допущенный дефект может быть обнаружен намного позже, уже в продакшене. Всё это приводит к дополнительным рискам, замедляет и удорожает цикл разработки.</p><p>Но существует и другая, не столь широко известная метрика, которая более объективно описывает надёжность проведённых тестов: она определяется в процессе <b>мутационного тестирования</b>. Первые упоминания об этом подходе мы встречаем еще в 1970-х годах. Тогда он уже признавался перспективным, но в то же время —  практически неприменимым из-за запредельного объема работы. Сегодня мы имеем возможность автоматизировать большую часть этой работы с помощью инструментов, например, Stryker.NET.</p><h2>Принципы мутационного тестирования</h2><p>Алгоритм мутационного тестирования достаточно прост: в исходный код (базу) вносятся различные изменения (мутации). Существует несколько разновидностей мутаций. Основные из них:</p><p>– <b>изменение операторов</b>: замена арифметических и логических операторов (например, + на -, &gt;= на &gt;),</p><p>– <b>изменение значений</b>: замена булевых значений, удаление вызовов методов или изменение литералов,</p><p>– <b>изменение условий</b>: в if, циклах и логических выражениях.</p><p>Затем на этом модифицированном коде выполняются модульные тесты для проверки их чувствительности к изменениям. Мутанты, которые вызывают провал тестов, считаются «убитыми» (Killed Mutants), остальные —  «выжившими». По итогу проверки формируется отчет, где ключевая метрика качества тестов —  Mutation Score —  доля убитых мутантов от их общего количества. Если тесты не «убивают» подавляющее число мутантов, значит их нельзя считать достаточно эффективными.</p><p>Среди инструментов для автоматизации проведения мутационного тестирования мы остановили выбор на Stryker.NET и вот, почему:</p><p>– на данный момент Stryker.NET обладает <b>наибольшим объемом автоматизированных операций</b>: он самостоятельно вносит мутации в исходный код, запускает юнит-тесты и генерирует подробные отчеты;</p><p>– Stryker.NET <b>полностью интегрирован с .NET-экосистемой</b>: поддерживает .NET и .NET Framework, основные тестовые фреймворки (xUnit, NUnit, MSTest);</p><p>– Stryker.NET – это бесплатный <b>инструмент с открытым исходным кодом</b>, постоянно обновляемый и поддерживаемый сообществом, что гарантирует его актуальность и развитие.</p><p>По нашей практике, Stryker.NET будет полезен для трех категорий пользователей:</p><p>– <b>Разработчик</b> может убедиться, что новый код покрыт качественными тестами и изменения не привели к деградации существующих тестов, а также находить и удалять тесты, которые ничего не тестируют и только отнимают ресурсы.</p><p>– <b>Ревьюер</b> может быстро и надежно проверить качество тестов в pull request.</p><p>– <b>ИТ-архитектор и руководитель разработки</b> могут регулярно строить и анализировать отчеты, чтобы мониторить «здоровье» юнит-тестов во всей кодовой базе.</p><h2>Установка и использование Stryker.NET</h2><p>Технически Stryker.NET представляет из себя dotnet tool. Соответственно, чтобы его установить, необходимо выполнить команду в cmd или в PowerShell консоли:</p><p><i>dotnet tool install -g dotnet stryker</i></p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/29e01f48-c311-41e1-a22b-b0be87e83f32.png" alt="" /><figcaption>установка Stryker.NET</figcaption></figure><p>Получение и анализ отчетов: результаты выводятся в консоль и сохраняются в подробном HTML-отчете.</p><p>Stryker.NET поддерживает несколько сценариев, простейший из них — анализ проекта с кодом. В этом случае необходимо запустить мутационное тестирование из папки, где расположен файл проекта, указать имя этого файла без пути и через ключ <b><i>tp</i></b> указать полные пути ко всем файлам проектов, содержащим тесты для проекта с кодом. Например:</p><p><i>dotnet stryker -p “project.csproj” -tp “c:\git\project.tests\project.tests.csproj”</i></p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/19df0a95-2009-4c70-b05b-c9fc49c9da8c.png" alt="" /><figcaption>запуск Stryker.NET для анализа проекта с кодом</figcaption></figure><p>В результате тестирования программа сгенерирует отчет, где в первой колонке будет выведена метрика Mutation Score в целом по проекту и по отдельным файлам, причем разделённая на две группы: <i>Of total</i> —  общее количество мутантов, <i>Of covered</i> —  количество мутантов, находящихся в тех фрагментах кода, для которых вообще существуют тесты.</p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/2d7165e7-c7b8-4f67-ac0a-bcbab2514419.png" alt="" /><figcaption>детальный отчет Stryker.NET файлов проекта, метрики</figcaption></figure><p>В колонке <i>Killed</i> отображается количество убитых мутантов, в колонке <i>Survived</i> —  количество выживших. В колонке <i>Timeout</i> —  количество мутантов, которые привели к зацикливанию выполнения тестов и их прерыванию утилитой. Значения остальных колонок, а также другую важную информацию можно посмотреть в документации на официальном сайте <a href="https://stryker-mutator.io/docs/stryker-net/introduction/">https://stryker-mutator.io/docs/stryker-net/introduction/</a>.</p><p>Также важно отметить, что отчёт доступен для более глубокого и детализированного анализа: можно раскрыть параметры каждого файла и увидеть подробную информацию о том, какие именно изменения были произведены, какие из мутантов выжили и почему.</p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/c2f8db51-6aaa-4dc9-8fa2-10bee3457fef.png" alt="" /><figcaption>красная точка показывает часть кода с выжившим мутаном</figcaption></figure><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/e1b90d3b-d5fa-44e9-b39e-7c7e847a9287.png" alt="" /><figcaption>кликнув на красную точку, можно увидеть, какая именно мутация выжила</figcaption></figure><p>Stryker.NET умеет генерировать отчеты не только в HTML, но и в других форматах: например, в JSON, который очень удобен для автоматического анализа, если разработчики планируют встроить этот инструмент в свой CI/CD-пайплайн. Кроме того, есть встроенный дашборд, который, к сожалению, невозможно развернуть локально —  он доступен только как online сервис (https://dashboard.stryker-mutator.io).</p><h2>Применение Stryker.NET при разработке банковских продуктов</h2><p>Мы внедрили использование Stryker.NET в проекте для крупного банка, чтобы с помощью этого инструмента решить <b>ряд взаимосвязанных задач</b>. Самая главная проблема заключалась в том, что мы хотели улучшить качество тестов посредством мутационного тестирования, но его проведение вручную —  предельно утомительная и требующая много времени процедура. Ни Product Owners, ни разработчики не были готовы постоянно выделять на это ресурсы.</p><p>Без регулярного мутационного тестирования, нам было практически невозможно оценить текущее состояние всей кодовой базы с точки зрения качества unit тестов. У нас было много unit тестов, мы имели высокий процент Code Coverage, но не знали, насколько эти тесты нас защищают. В свою очередь, без понимания текущего состояния у нас не было возможности устанавливать команде цели по улучшению ситуации.</p><p>На данный момент команда активно <b>использует Stryker.NET на всех этапах разработки</b>.</p><p>Мы столкнулись и с ограничением использования Stryker.NET: попытка его интеграции в CI/CD-пайплайн оказалась неудачной. Это связано с тем, что кодовая база проекта насчитывает несколько миллионов строк, и выполнение всех проверок занимает примерно 12 часов. Однако мы думаем, что на проектах меньшего размера такая интеграция должна сработать.</p><p>Мы выбрали альтернативный вариант: был внедрен регулярный автоматический пост-релизный прогон Stryker.NET по ветке master, в результате которого генерируется сводный отчет. Проводится анализ изменений сводного отчета по всем проектам от релиза к релизу. По данным каждого сводного отчета мы можем оценивать работу конкретных проектных команд, и если их метрика Mutation Score недостаточно высока, – ставить цели по улучшению.</p><figure><img src="https://media.tproger.ru/user-uploads/117400/2025-10-07/cb80b102-c01a-4ed2-8892-af8630d0281f.png" alt="" /><figcaption>сводный отчет по всем проектам solution'на</figcaption></figure><p><b>Резюме</b><b></b></p><p>Итак, мутационное тестирование – мощный инструмент для повышения качества юнит-тестов и, соответственно, качества кода. Оно позволяет не только узнать, какой процент кода покрыт тестами, но и убедиться в том, что эти тесты действительно защищают код от ошибок.</p><p>Внедрение Stryker.NET —  шаг к более надёжной и предсказуемой разработке. Его регулярное использование помогает уверенно вносить изменения, рефакторить код и добавлять новый функционал.</p><p>Мы рекомендуем начать использование Stryker.NET в ваших проектах с ключевых модулей. При этом стоит постоянно делиться опытом с командой, чтобы наиболее эффективно улучшать программный продукт.</p>]]></content:encoded>
    </item>
    <item>
      <title>Руководство по промптам GPT-5: практики для агентов, кодирования и управляемости</title>
      <link>https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti</link>
      <comments>https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Даровская Маша]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti</guid>
      <description><![CDATA[<p>Руководство по промптам GPT-5: практики для агентов, кодирования и управляемости от OpenAI. Готовые шаблоны промптов, настройка reasoning_effort, работа с Responses API и советы по созданию приложений. Повысьте стабильность и эффективность ваших ИИ-решений.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/rukovodstvo-po-promptam-gpt-5--praktiki-dlya-agentov--kodirovaniya-i-upravlyaemosti">Руководство по промптам GPT-5: практики для агентов, кодирования и управляемости</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Markdown]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Промпты]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 07 Oct 2025 14:00:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>Мы перевели <a href="https://cookbook.openai.com/examples/gpt-5/gpt-5_prompting_guide">статью</a> Ануп Кота, Джулиан Ли, Эрика Закариассон из OpenAI. Статья написана для разработчиков агентных систем, инженеров ИИ-продуктов, команд фронтенда/бэкенда, редакторов кода с ИИ.</p><p>GPT-5 — флагманская reasoning-модель с упором на агентные сценарии, кодинг, интеллект и управляемость. «Из коробки» она хорошо решает широкий спектр задач, но качественные промпты (подсказки) заметно повышают стабильность, скорость и соблюдение инструкций. Ниже — проверенные практики, готовые шаблоны и заметки по параметрам API (reasoning_effort, verbosity, Responses).</p><h2>Предсказуемость агентного рабочего процесса</h2><h2>Используйте API Responses для агентов</h2><p>Responses сохраняет логические следы между вызовами инструментов: это снижает задержку, экономит токены и повышает качество планов за счёт механизма previous_response_id.</p><h2>Контролируйте «рвение» агента</h2><p>Сдержанный режим (меньше вызовов, ниже задержки):</p><ul><li>Установите reasoning_effort=low|medium.</li><li>В подсказке ограничьте глубину поиска контекста и задайте ранние критерии остановки.</li></ul><h2>Проактивный режим (больше автономии и настойчивости):</h2><ul><li>Поднимите reasoning_effort.</li><li>Включите явное требование «не возвращаться к пользователю до полного решения».</li></ul><p>Если вы готовы к максимально строгому регулированию, то можете установить фиксированный бюджет на вызовы инструментов, как показано ниже. Бюджет, естественно, может варьироваться в зависимости от желаемой глубины поиска.</p><p>При ограничении основного поведения сбора контекста полезно явно предоставить модели запасной вариант, облегчающий выполнение более короткого этапа сбора контекста. Обычно это делается в виде условия, позволяющего модели продолжать работу в условиях неопределенности, как «even if it might not be fully correct» в примере выше.</p><p>С другой стороны, если вы хотите поощрить автономность модели, увеличить настойчивость в вызове инструментов и сократить количество уточняющих вопросов или иных случаев возврата информации пользователю, рекомендуется увеличить reasoning_effort и использовать такой промпт, чтобы поощрить настойчивость и тщательное выполнение задачи:</p><p>Хорошая практика — чётко указать условия остановки задач агента, обозначить безопасные и небезопасные действия и определить, когда, если это вообще возможно, модель может вернуть данные пользователю. Например, в наборе инструментов для покупок инструменты оформления заказов и оплаты должны явно иметь более низкий порог неопределённости, требующий пояснений пользователя. В то же время инструмент поиска должен иметь чрезвычайно высокий порог; аналогично, в конфигурации кодинга инструмент удаления файлов должен иметь гораздо более низкий порог, чем инструмент поиска grep.</p><h2>«Преамбулы» к инструментам (чтобы пользователь понимал, что происходит)</h2><p>GPT-5 обучен предоставлять чёткие предварительные планы и последовательные обновления о ходе работы с помощью сообщений «преамбулы инструмента».</p><p>Вы можете управлять частотой, стилем и содержанием преамбул инструментов в вашем запросе — от подробных объяснений каждого вызова инструмента до краткого предварительного плана. Вот пример качественной преамбулы:</p><p>Вот пример преамбулы инструмента, которая может быть выведена в ответ на такой запрос. Такие преамбулы могут значительно улучшить способность пользователя следить за работой вашего агента по мере её усложнения:</p><h2>Усилия рассуждения и повторное использование контекста</h2><ul><li>reasoning_effort регулирует интенсивность размышлений и готовность к вызову инструментов. Значение по умолчанию — medium. Но для многошаговых задач повышайте этот показатель.</li><li>Разбивайте сценарий на несколько ходов агента с сохранением previous_response_id в Responses — модель не тратит токены на перестройку плана.</li><li>Настоятельно рекомендуют использовать API Responses в GPT-5, чтобы улучшить потоки агентов, снизить затраты и повысить эффективность токенов в приложениях. Авторы отмечают, что наблюдали статистически значимые улучшения в оценках при использовании API Responses по сравнению с завершением чата. Например, рост оценки Tau-Bench Retail с 73,9% до 78,2% был только благодаря переходу на API Responses и включению previous_response_id (функции передачи предыдущих элементов рассуждений в последующие запросы). Это позволяет модели ссылаться на предыдущие трассировки рассуждений, экономя токены и устраняя необходимость перестраивать план с нуля после каждого вызова инструмента, что снижает latency. Эта функция доступна всем пользователям API Responses.</li></ul><h2>Максимизация продуктивности в кодинге</h2><p>GPT-5 умеет работать с крупными кодовыми базами, накатывать многофайловые изменения, рефакторить и строить новые приложения.</p><h2>Рекомендованный стек для фронтенд-приложений</h2><ul><li>Фреймворк: Next.js (TypeScript), React, HTML</li><li>Стили/UI: Tailwind CSS, shadcn/ui, Radix themes</li><li>Иконки: Lucide / Heroicons / Material Symbols</li><li>Анимации: Framer Motion</li><li>Шрифты: Inter, Geist, Mona Sans, IBM Plex Sans, Manrope</li></ul><h2>Разработка приложений с нуля</h2><p>GPT-5 отлично подходит для создания приложений за один раз. В ходе ранних экспериментов с моделью пользователи обнаружили, что подсказки, подобные приведённой ниже, — где модель итеративно выполняет задания, используя самостоятельно разработанные критерии качества, — повышают качество результатов благодаря использованию возможностей GPT-5 в области тщательного планирования и самоанализа.</p><h2>Соответствие стандартам разработки</h2><p>При внедрении постепенных изменений и рефакторинга в приложения, код, написанный на основе модели, должен соответствовать стандартам стиля и дизайна и максимально аккуратно вписываться в кодовую базу. Без специальных подсказок GPT-5 автоматически ищет справочный контекст в кодовой базе, например, читая package.json для просмотра уже установленных пакетов. Но это поведение можно улучшить с помощью подсказок, обобщающих ключевые аспекты, такие как принципы разработки, структура каталогов и передовой опыт кодовой базы.</p><p>Фрагмент подсказки ниже демонстрирует один из способов организации правил редактирования кода для GPT-5: не стесняйтесь изменять фактическое содержание правил в соответствии со своими предпочтениями в программном дизайне.</p><h2>Форматирование Markdown</h2><p>По умолчанию GPT-5 в API не форматирует свои окончательные ответы в Markdown, чтобы обеспечить максимальную совместимость с разработчиками, чьи приложения могут не поддерживать рендеринг Markdown. Тем не менее, запросы, подобные следующему, в значительной степени успешно обеспечивают иерархические окончательные ответы в Markdown.</p><p>Иногда соблюдение инструкций Markdown, указанных в системном запросе, может ухудшаться в течение длительного разговора. Если вы столкнулись с такой ситуацией, стабильное соблюдение инструкций Markdown будет работать при добавлении их к каждому 3–5 пользовательскому сообщению.</p>]]></content:encoded>
    </item>
    <item>
      <title>LSP-плагины в IntelliJ теперь работают бесплатно и без Ultimate-подписки</title>
      <link>https://tproger.ru/news/--lsp-plaginy-v-intellij-teper-rabotayut-besplatno-i-bez-ultimate-podpiski</link>
      <comments>https://tproger.ru/news/--lsp-plaginy-v-intellij-teper-rabotayut-besplatno-i-bez-ultimate-podpiski?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--lsp-plaginy-v-intellij-teper-rabotayut-besplatno-i-bez-ultimate-podpiski</guid>
      <description><![CDATA[<p>JetBrains сделала поддержку LSP-плагинов в IntelliJ бесплатной: с версии 2025.2 они работают и без подписки Ultimate, в fallback-режиме</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--lsp-plaginy-v-intellij-teper-rabotayut-besplatno-i-bez-ultimate-podpiski">LSP-плагины в IntelliJ теперь работают бесплатно и без Ultimate-подписки</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Intel]]></category>
      <category><![CDATA[JetBrains]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 02 Sep 2025 04:05:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>JetBrains открыла поддержку LSP-плагинов в бесплатном режиме IntelliJ IDEA.</p><p>Это значит, что даже без подписки Ultimate вы сможете использовать и разрабатывать плагины на основе Language Server Protocol.</p><h2>Что произошло?</h2><p>JetBrains продолжает переход к <b>единой дистрибуции</b> IntelliJ IDEA — начиная с версии 2025.3, будет один установщик для всех пользователей, вне зависимости от подписки.</p><p>Уже в версии 2025.2 вступает в силу новый <b>fallback-режим</b>. Это бесплатный уровень для пользователей с истекшей подпиской, в котором доступен ограниченный набор возможностей IDE.</p><p>Одна из ключевых новостей: <b>поддержка LSP API теперь входит в этот бесплатный набор</b>. Ранее такая возможность была только в Ultimate-редакции, но впредь плагины на основе LSP можно запускать и без платной лицензии.</p><h2>Что это значит для разработчиков плагинов?</h2><p>Если вы пишете плагины на базе <b>Language Server Protocol</b>, они станут доступны куда более широкой аудитории — не только платным пользователям IntelliJ Ultimate, но и тем, кто работает в бесплатном режиме.</p><p>Важно: <b>поддержка LSP исчезнет из Community Edition</b> (она будет выведена из обращения после релиза 2025.2), так что fallback-режим станет основным способом бесплатного использования LSP в IntelliJ. Чтобы использовать LSP в своих плагинах, нужно:</p><ul><li>Таргетить IntelliJ IDEA Ultimate 2025.2.1 или новее.</li><li>Указать опциональную зависимость на модуль com.intellij.modules.lsp.</li></ul><h2>Что умеет LSP в IntelliJ?</h2><p>LSP API в платформе IntelliJ уже поддерживает:</p><ul><li>Подсказки с resolve-поддержкой.</li><li>Переход к определению.</li><li>Документацию при наведении.</li><li>Диагностику.</li><li>Code actions и быстрые фиксы.</li><li>Форматирование документов.</li></ul><p>В то же время представители JetBrains подчеркивают, что <b>LSP — это не замена PSI</b>. Встроенная PSI-система остается основой глубокой интеграции языков в IDE и именно она обеспечивает расширенные функции вроде рефакторинга, анализа кода и т.д.</p><p>LSP — более универсальный, но менее производительный и менее гибкий подход.</p><h2>Что дальше?</h2><p>Полный переход случится с релизом 2025.3, когда Community Edition перестанет существовать как отдельная сборка, а fallback-режим станет основной бесплатной точкой входа.</p><p>Уже сейчас разработчики плагинов могут адаптироваться к новым условиям и проверять, как их LSP-плагины работают в новом режиме.</p>]]></content:encoded>
    </item>
    <item>
      <title>Разработчик рассказал, как ИИ ускоряет обучение, но может «украсть» опыт</title>
      <link>https://tproger.ru/news/razrabotchik-rasskazal--kak-ii-uskoryaet-obuchenie--no-mozhet--ukrast--opyt</link>
      <comments>https://tproger.ru/news/razrabotchik-rasskazal--kak-ii-uskoryaet-obuchenie--no-mozhet--ukrast--opyt?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/razrabotchik-rasskazal--kak-ii-uskoryaet-obuchenie--no-mozhet--ukrast--opyt</guid>
      <description><![CDATA[<p>Разработчик предупредил, что ИИ ускоряет обучение, если быть активным учеником, но при бездумном использовании «крадет» опыт и понимание системы</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/razrabotchik-rasskazal--kak-ii-uskoryaet-obuchenie--no-mozhet--ukrast--opyt">Разработчик рассказал, как ИИ ускоряет обучение, но может «украсть» опыт</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 01 Sep 2025 03:12:16 GMT</pubDate>
      <content:encoded><![CDATA[<p>ИИ может помочь программисту учиться быстрее. Но если использовать его бездумно, можно потерять важную часть опыта, <a href="https://allvpv.substack.com/p/turn-off-cursor-turn-on-your-mind">считает</a> Пшемыслав Кусяк — разработчиков из Польши, опубликовавший личную колонку на Substack.</p><p>Он делит подходы к ИИ на два лагеря и предупреждает об опасностях одного из них.</p><h2>Два подхода: помощник и костыль</h2><p>Разработчик предлагает разделить все способы работы с ИИ на два условных сценария.</p><p><b>Первый — использовать ИИ как обучающего ассистента:</b> задавать вопросы, уточнять непонятное, просить примеры, искать лучшие практики. Такой подход помогает изучать технологии, язык программирования и архитектуру системы. И, по мнению автора, делает разработчика сильнее и осознаннее.</p><p><b>Второй подход — перекладывать задачи на ИИ, не вникая в суть.</b> Например, просить сгенерировать реализацию, не проверяя ее, или доверять рефакторинг и отладку без разбора. Это ускоряет работу, но одновременно и снижает вовлеченность и понимание.</p><blockquote>Если ИИ делает всю работу за вас, вы не учитесь ничему новому. Вы просто теряете шанс разобраться в системе.</blockquote><h2>Учеба — это неотъемлемая часть кодинга</h2><p>Разработчик приводит простой пример: задача, которую вы берете в первый день на новом проекте, будет решаться дольше, чем такая же по сложности задача через месяц. Все потому, что в процессе работы вы не просто решаете задачи, а <b>формируете ментальную карту всей системы</b>.</p><p>В этом и заключается суть разработки: <b>постоянное обучение и понимание контекста</b>. Именно это позволяет находить лучшие решения и писать надежный код. А не просто автоматическая генерация функций.</p><h2>ИИ может «украсть» этот путь</h2><p>Автор отмечает: с появлением так называемого <b>агентного кодинга</b> (agentic coding), где ИИ-инструменты вроде Cursor и Claude Code могут взять задачу «под ключ», часть инженеров рискуют <b>потерять контроль над кодом и проектом</b>.</p><p>Да, ИИ может сгенерировать два прототипа за 15 минут. Но сможете ли вы полноценно оценить их качество? Сможете ли быстро внести изменения, если не понимаете, как все устроено?</p><blockquote>Я не могу сказать «это ошибка ИИ» — ответственность все равно на мне. Поэтому я лучше сначала сам реализую решение, а уже потом спрошу ИИ, как его улучшить.</blockquote><h2>Опасный соблазн скорости</h2><p>Автор признает: ИИ действительно ускоряет многие рутинные процессы. Написание документации, юнит-тестов, работа с исключениями — все это стало быстрее.</p><p>Но именно <b>такая легкость</b> может ввести в заблуждение: вроде бы все работает, а значит, все хорошо. На деле — растет технический долг и снижается квалификация.</p><p>Он приводит аналогию: если ИИ может заменить троих инженеров за выходные, кто из них получит больше знаний — тот, кто провел три дня с ИИ, или тот, кто шесть месяцев вникал в проект?</p><h2>Вывод: ИИ — не замена, а партнер</h2><p>Автор подчеркивает, что не отрицает пользу ИИ. Он сам использует ассистентов, чтобы проверить гипотезы, получить советы, изучить новые подходы. Но <b>не доверяет им слепо</b> и не передает всю ответственность.</p>]]></content:encoded>
    </item>
    <item>
      <title>GPT-5 — самый умный ИИ в истории. Рассказываем, почему это не просто маркетинг</title>
      <link>https://tproger.ru/news/gpt-5---samyj-umnyj-ii-v-istorii--rasskazyvaem--pochemu-eto-ne-prosto-marketing</link>
      <comments>https://tproger.ru/news/gpt-5---samyj-umnyj-ii-v-istorii--rasskazyvaem--pochemu-eto-ne-prosto-marketing?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/gpt-5---samyj-umnyj-ii-v-istorii--rasskazyvaem--pochemu-eto-ne-prosto-marketing</guid>
      <description><![CDATA[<p>GPT-5 — не просто языковая модель, а агент, мыслящий через инструменты: он решает задачи, как инженер, и действует почти как человек</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/gpt-5---samyj-umnyj-ii-v-istorii--rasskazyvaem--pochemu-eto-ne-prosto-marketing">GPT-5 — самый умный ИИ в истории. Рассказываем, почему это не просто маркетинг</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 08 Aug 2025 08:06:07 GMT</pubDate>
      <content:encoded><![CDATA[<p>OpenAI выпустила GPT-5, <a href="https://www.youtube.com/watch?v=FVejMWCh9nI">посмотреть презентацию можно здесь</a>. По мнению первых разработчиков, которые протестировали новинку, это шаг, максимально близкий к полноценному искусственному интеллекту (AGI).</p><p>Главное отличие новой модели — не просто способность давать ответы, а умение <b>использовать инструменты так, как это делает человек</b>.</p><p>Похоже на то, что GPT-5 уже не просто языковая модель — это <b>агент</b>, способный планировать, исследовать и действовать.</p><h2>GPT-5 думает с помощью инструментов</h2><p>Эксперты из Latent.Space <a href="https://www.latent.space/p/gpt-5-review">отметили</a>, что GPT-5 демонстрирует новый уровень интеграции с внешними средствами: от баз данных и интерпретаторов кода до веб-поиска и интерфейсных API.</p><p>Главное — модель не просто вызывает инструменты, а <b>понимает, когда и зачем их использовать</b>, может запускать их параллельно и адаптироваться по ходу задачи.</p><p>Это делает GPT-5 особенно сильным в инженерных задачах. Например, она смогла с первого раза устранить сложные конфликты зависимостей в реальном проекте, где другие модели (Claude, o3, Cursor) справиться не смогли.</p><p>Кроме того, GPT-5 успешно создаёт полноценные веб-приложения «с нуля» — от HTML и CSS до баз данных, причём с минимальными подсказками от пользователя.</p><h2>GPT-5 — не лучший писатель, но идеальный инженер</h2><p>Парадоксально, но GPT-5 проигрывает предшественникам вроде GPT-4.5 в задачах творчества и письма.</p><p>Там, где GPT-4.5 сохраняет стиль и тон автора, новая модель выдает слишком «рыночные» и шаблонные ответы. Однако в инженерных задачах — от рефакторинга до генерации сложных SQL-запросов — GPT-5 не знает себе равных.</p><p>Разработчики называют GPT-5 «практичной» моделью: она буквальна, понятлива и предсказуема. Ей можно задавать чёткие цели, и она будет выполнять их последовательно и точно.</p><h2>Агентская эпоха наступила</h2><p>Авторы Latent.Space называют выход GPT-5 «началом <b>каменного века для агентов</b>». Аналогия: человечество стало разумным, когда научилось использовать инструменты. Так же и GPT-5 — она <b>мыслит через инструменты</b>.</p><p>То есть ИИ не просто выполняет промт. Теперь модель — это полноценная система, с которой можно работать как с живым сотрудником: давать инструкции, контекст, цель и критерии успешности.</p><h2>Демо GPT-5 на русском</h2>]]></content:encoded>
    </item>
    <item>
      <title>Типизированная навигация в React Router</title>
      <link>https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router</link>
      <comments>https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Михаил Сахаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router</guid>
      <description><![CDATA[<p>Когда прочитаете эту статью, сможете настроить типобезопасную навигацию в своем проекте, забудете про сломанные ссылки после рефакторинга и перестанете нервничать на релизах.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/tipizirovannaya-navigaciya-v-react-router">Типизированная навигация в React Router</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 16 Jul 2025 10:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Типизированная навигация в React Router решает классические проблемы фронтенд-разработки: опечатки в путях, сломанные ссылки после рефакторинга и отсутствие автокомплита. Это полезно и джунам, которые хотят избежать глупых ошибок, и сеньорам, проектирующим большие проекты. Инструмент превращает строковые пути в типобезопасную систему навигации.</p><p>Представьте: пятница, 18:30. Релиз через час. Вы меняете один роут в конфиге — и внезапно половина приложения отлетает. Поздравляем, вы только что познакомились с классической болью фронтендеров.</p><p>Проблема кроется в самой природе JavaScript. Строковые пути вроде /users/profile/${id} существуют в коде как обычные строки — без проверок, автокомплита и гарантий корректности. Опечатался в /usres вместо /users — и твоя навигация сломалась, а TypeScript молчит как рыба.</p><p>Двадцать лет назад мы кликали по window.location.href, десять лет назад — радовались React Router, а сегодня пора переходить на типизированные решения.</p><p>Когда прочитаете эту статью, сможете настроить типобезопасную навигацию в своем проекте, забудете про сломанные ссылки после рефакторинга и перестанете нервничать на релизах.</p><h2>Суть проблемы</h2><p>Проблема очевидна: путь /admin/products размазан по всему коду. TypeScript не знает, что эти строки связаны с определенной директорией, поэтому не проверяет их корректность. Опечатка в product вместо products — и вылетает ошибка 404.</p><p>В больших проектах эта проблема критична. Приложение с 200+ путями, где навигация разбросана по сотне компонентов, превращается в минное поле. Один программист меняет структуру URL, а остальные даже не подозревают об этом. Команды используют TypeScript для типобезопасности, но навигация остается уязвимой.</p><h2>Что такое типизированная навигация?</h2><p>Типизированная навигация превращает строковые пути в типизированные объекты. Вместо /admin/products/123/edit программист работает с функциями, которые знают структуру приложения и проверяют корректность написания путей на этапе компиляции.</p><p>Представьте GPS-навигатор, который знает все адреса в городе. Вы не можете ввести несуществующую улицу — система сразу выдаст ошибку. Так работает типизированная навигация: TypeScript проверяет, что путь существует, параметры переданы правильно, а структура URL соответствует пути.</p><p>Три ключевых преимущества: автокомплит в IDE, проверка на этапе компиляции и безопасный рефакторинг. Поменяете пути в коде — TypeScript сразу покажет все места, которые нужно обновить.</p><p>Польза зависит от уровня разработчика:</p><ul><li>Джуны получают защиту от опечаток и автокомплит — меньше глупых ошибок и быструю разработку.</li><li>Миддлы ускоряют разработку благодаря надежному рефакторингу — можно смело менять структуру URL без страха что-то сломать.</li><li>Сеньоры используют типизацию для построения архитектуры приложения — создают переиспользуемые компоненты навигации, проверяют параметры и строят масштабируемые системы путей и директорий.</li></ul><p>Типизированная навигация превращает хрупкий код в надежную систему, где ошибки находятся до деплоя.</p><h2>Как использовать React Router вместе с TypeScript</h2><p>Для базовой типизации в React Router v6 сначала определите структуру путей. Создайте интерфейс, который описывает все пути в приложении:</p><p>Типизация параметров URL решает проблему с useParams. Вместо any получаете конкретные типы:</p><p>Query-параметры типизируются аналогично через useSearchParams. Создайте интерфейс для каждой страницы с query-параметрами и оберните хук.</p><p>Так система будет дополнять код в IDE, проверять все пути на этапе компиляции и защитит от опечаток. Полчаса настройки сэкономят вам часы отладки и целый вагон нервов.</p><h2>Как внедрить типизированную навигацию в проекты</h2><p>Централизованная система маршрутов облегчает управление навигацией в больших приложениях. Создайте отдельный файл с конфигурацией всех путей:</p><p>Конфигурация избавит от нужды дублировать код при генерации типов. TypeScript автоматически выведет все возможные пути и их параметры из одного объекта.</p><p>Современные библиотеки решают проблему из коробки.<a href="https://tanstack.com/router"> Например, Tanstack Router</a> предоставляет полностью типизированную систему путей с автогенерацией типов, а<a href="https://github.com/typehero/type-route"> Type-route</a> создает типобезопасные пути через API.</p><p>Библиотека<a href="https://github.com/typesafe-routes/typesafe-routes"> typesafe-routes</a> внедряется даже в крупные проекты без необходимости менять сотни строк кода.</p><p>Выбор инструмента зависит от размера программы. Если у вас небольшое приложение — быстрее написать хук в пару строк. Но если разрабатываете сложный сервис — используйте библиотеки.</p><h2>Как не сломать код при использовании типизированной навигации</h2><p>Не пытайтесь переписать весь проект за раз — создайте типизированные хуки для новых фич, а старый код обновляйте по мере рефакторинга.</p><p>Чеклист для код-ревью поможет не уронить прод:</p><ul><li>Все новые navigate() и  используют типизированные версии;</li><li>Параметры путей явно типизированы;</li><li>Нет магических строк в навигации;</li><li>Query-параметры описаны интерфейсами.</li></ul><p>Важно: не используйте одновременно строки и типизированные пути — выберите один подход для проекта и не допускайте высокого уровня вложенности в объектах.</p><p>Автоматизация упрощает процесс. ESLint правило no-hardcoded-routes запретит использование строк в навигации. Код-генераторы создают типы из OpenAPI схем или конфигурации роутера.</p><p>Производительность не страдает — типы исчезают после компиляции. Теряется немного времени на компиляцию TypeScript, но экономия на отладке перекрывает затраты.</p><h2>Где применять типизированную навигацию</h2><p>SPA-приложения получают максимальную пользу от типизированных путей. В дашбордах с десятками страниц навигация становится еще важнее — один сломанный путь может уронить проект.</p><p>E-commerce проекты выигрывают от типизации каталогов и фильтров. Пути вроде /catalog/:category/:subcategory?filters=price,brand содержат много параметров, которые легко сломать при рефакторинге.</p><p>Интеграция с Redux и Zustand упрощает синхронизацию состояния с URL. Типизированные селекторы автоматически обновляются при изменении роутов:</p><p>Вложенные директории требуют особого внимания. Каждый уровень вложенности усложняет типизацию — планируйте структуру заранее, а не рефакторьте задним числом.</p><p>Типизированная навигация — стандарт современной разработки. Команды, которые до сих пор полагаются на строковые пути, тратят лишнее время на поиск багов. Программисты увереннее рефакторят код, не боятся мелких ошибок и не роняют прод в пятницу вечером из-за одного символа в пути.</p>]]></content:encoded>
    </item>
    <item>
      <title>Java → Rust → 0% готовности: как разработчик за 7 лет так и не дошел до MVP своего проекта</title>
      <link>https://tproger.ru/news/java---rust---0--gotovnosti--kak-razrabotchik-za-7-let-tak-i-ne-dowel-do-mvp-svoego-proekta</link>
      <comments>https://tproger.ru/news/java---rust---0--gotovnosti--kak-razrabotchik-za-7-let-tak-i-ne-dowel-do-mvp-svoego-proekta?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/java---rust---0--gotovnosti--kak-razrabotchik-za-7-let-tak-i-ne-dowel-do-mvp-svoego-proekta</guid>
      <description><![CDATA[<p>Разработчик 7 лет переписывал проект с Java на Rust — и так и не дошёл до MVP. Теперь он признает: без дисциплины, фокуса и приоритизации даже лучший код — пустышка</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/java---rust---0--gotovnosti--kak-razrabotchik-za-7-let-tak-i-ne-dowel-do-mvp-svoego-proekta">Java → Rust → 0% готовности: как разработчик за 7 лет так и не дошел до MVP своего проекта</a>»</p>]]></description>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[iOS]]></category>
      <category><![CDATA[Swift]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 09 Jun 2025 07:25:30 GMT</pubDate>
      <content:encoded><![CDATA[<p>7 июня 2025 года разработчик под ником @jonhoo <a href="https://www.fossable.org/projects/sandpolis/7-years-of-development/">опубликовал</a> пост в честь годовщины проекта Sandpolis — инструмента удаленного администрирования, который он разрабатывает… с 2018 года. За это время проект так и не дошел даже до стадии MVP.</p><p>Вместо успеха — выгорание, фатальный рефакторинг и отсутствие дисциплины. Ниже — краткий пересказ его откровения и важных уроков.</p><h2>Как все начиналось</h2><p>Sandpolis задумывался как универсальный инструмент для системных администраторов. Идея пришла в университете, энтузиазма хватало. Уже в 2019 году у проекта была рабочая Java-серверная часть (~50 000 строк кода) и небольшой iOS-клиент на Swift (~10 000 строк).</p><p>Но затем случился <b>«переписон»</b> — автор решил, что все нужно перевести на Rust, «язык будущего». Так началась вторая жизнь проекта. К сожалению, безрезультатная.</p><h2>Почему за 7 лет не получилось даже MVP</h2><p>Вот как сам автор описывает главные причины провала:</p><ul><li><b>Переписывание ради языка.</b> Rust стал увлечением, и проект превратился в площадку для экспериментов, а не развития продукта.</li><li><b>Фокус на «интересном», а не важном.</b> Вместо ключевых проблем (например, проработки модели данных) автор занимался второстепенными деталями: паролями, мелкой оптимизацией.</li><li><b>Дыры вместо структуры.</b> Поскольку сложные задачи откладывались, проект обрастал кусками кода, которые не складывались в единую систему.</li><li><b>Отсутствие дисциплины.</b> Основной диагноз: нежелание делать трудное и скучное. Работа велась по принципу «где интересней» — но не «где нужней».</li></ul><h2>Что автор понял спустя 7 лет</h2><blockquote><i>Дисциплина — это то, чего в инженерии сегодня не хватает так же сильно, как хорошего вкуса.</i></blockquote><p>Основные выводы:</p><ul><li><b>Делай приоритетное — даже если оно скучное.</b> Не отвлекайся, пока не решишь главную задачу.</li><li><b>Чем дольше код сломан — тем сложнее его починить.</b> Правки должны возвращать проект в рабочее состояние как можно быстрее.</li><li><b>Не переписывай без причин.</b> Переписка кода на новом языке может уничтожить весь прогресс.</li><li><b>Ранние ошибки становятся дорогими.</b> Лучше «заплатить» временем сразу, чем страдать потом.</li><li><b>Идеи без реализации — это просто мечты.</b> Рабочий код важнее красивых концептов.</li></ul><h2>Что дальше</h2><p>Разработчик пообещал, что в 2025 году он сосредоточится только на одной вещи: <b>идеальной модели данных</b> для Sandpolis. А уже после — будет строить вокруг нее приложение.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI?</title>
      <link>https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-</link>
      <comments>https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Дима Державин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-</guid>
      <description><![CDATA[<p>Узнайте, как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI: генерация запросов, типизация и меньше ручной работы.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-uprostit-rabotu-s-api-v-react-prilozhenii-s-pomoshhyu-rtk-query-i-openapi-">Как упростить работу с API в React-приложении с помощью RTK Query и OpenAPI?</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 23 May 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Ручная интеграция API и веб-приложения часто приводит к проблемам с обратной совместимостью контрактов. На стороне фронтенда это приводит к падению приложения, на бекенде — к потере данных с фронтенда. Один из способов предотвратить такие сложности — кодген.</p><p>В статье мы разберёмся, как запустить кодген на основе OpenApi схемы для веб-приложения на стэке React + redux-toolkit.</p><p>Для справки: контракт — соглашение о формате данных между клиентом и сервером</p><h2>Какие проблемы решает кодген?</h2><p>Любой разработчик может совершить ошибку: неудачно разрезолвить мердж-конфликт, забыть предупредить о рефакторинге или банально опечататься.</p><p>В микро-сервисной архитектуре цена таких ошибок высока — оперативно доставить обновления многочисленным клиентам иногда бывает проблематично.</p><p><i>Кодген снижает вероятность таких ошибок, так как методы статично создаются на основе схемы, которая, в идеале, также должна генерироваться на основе кода бекенда. Рассмотрим подробнее.</i></p><h3>Синхронизация контрактов</h3><p>При активной разработке бекенд постоянно обновляет контракты. Без кодгена все обновления на стороне фронтенда синхронизируются вручную.</p><p>Также на основе контрактов создаются побочные интерфейсы, например, для redux-слайсов и react-компонентов. Ошибка в основном контракте создает цепочку проблем со связанными типами и интерфейсами.</p><p>Сочетание кодген-контрактов и typescript-утилит для создания побочных интерфейсов делает доставку обновлений простой и безопасной.</p><h3>Устранение дубликатов</h3><p>Когда проект не имеет четкой структуры, контракты оказываются разбросаны по всей кодовой базе. Разработчики, не сумев найти нужный интерфейс, просто создают свои варианты контрактов. Так рождаются дубликаты и несогласованность.</p><p><i>Кодген решает эти проблемы, создавая единую точку входа для всех методов и интерфейсов.</i></p><p><b>Совет:</b> Кодген проще и безопасней внедрять на начальных этапах разработки. А в уже написанных проектах — стоит внедрять его постепенно, например, по несколько эндпойнтов за раз.</p><h2>Что понадобиться для кодгена?</h2><p>Прежде чем запускать кодген нам нужно минимально настроить проект:</p><h3>Подготовить схему</h3><p>OpenAPI-схема в формате yaml:</p><p><b>Важно: </b>Любая ошибка в схеме приводит к поломке кодгена. Поэтому важно, чтобы ваша схема проходила валидацию.</p><p>Проверить валидность OpenAPI схемы можно на в <a href="https://editor.swagger.io">swagger редакторе</a></p><h3>Настроить Redux</h3><p>Redux-клиент для API-запросов:</p><p>Redux-слайс для хранения данных из API-запроса:</p><p>Redux-хранилище с подключенным API-клиентом и редюсером:</p><h3>Настроить кодген</h3><p>Для кодгена возьмём официальную библиотеку от Redux — <a href="https://www.npmjs.com/package/@rtk-query/codegen-openapi">@rtk-query/codegen-openapi</a></p><p><a href="https://www.npmjs.com/package/@rtk-query/codegen-openapi"></a>Затем создадим файл настройками кодгена:</p><p>Библиотека хороша тем что покрывает все основные потребности, позволяя быстро запустить когден без долгих настроек. Цена удобства — отсутствие гибкости. Адаптировать результат когдена под сложный кодстайл будет проблематично.</p><p>Если вам нужна гибкость, попробуйте использовать <a href="https://www.npmjs.com/package/@openapitools/openapi-generator-cli">@openapitools/openapi-generator-cli</a> с различными готовыми шаблонами или <a href="https://github.com/orval-labs/orval">Orval</a> от tanstack</p><h2>Запуск кодгена</h2><p>Для запуска добавим команду в package.json:</p><p>Далее заходим в корень проекта и запускаем команду:</p><p>На выходе должен получиться файл codegenApi.ts с содержимым:</p><p>Проверяем результат:</p><ul><li>Инджект кодген эндпойтов в baseApi;</li><li>Системные типы Props Response Error;</li><li>Контракт User;</li><li>RTK-query хуки.</li></ul><p><b>Совет: </b>создание кодгена можно автоматизировать, например, сделать его отдельным этапом в CI/CD, который выполняется прямо перед билдом приложения.</p><h2>Возможные проблемы</h2><h3>Необычные типы данных</h3><p>При внедрении кодгена в проект на Kotlin я столкнулся с проблемой парсинга: кодген не мог распарсить тип данных LocalDateTime, который должен возвращать строку с датой в формате ISO. Вместо строки когден возвращал пустой объект.</p><p>LocalDateTime — это класс из Java-библиотеки java.time, который представляет дату и время без учёта временной зоны.</p><p>Данную проблему я решил с помощью кодмода:</p><p>Кодмод запускается после команды codegen. Он бежит по файлу сверху вниз, находит нужный тип и меняет его значение на string.</p><p><b>Совет:</b> При добавлении кодмода в общий файл обязательно опишите проблему которую он решает. Когда кодмодов станет много и они начнут конфликтовать, вам будет проще разобраться в их работе.</p><p>Кодмоды отлично генерируются с помощью AI.</p><h2>Плюсы и минусы кодгена</h2><p>Плюсы:</p><ul><li>Генерируем API-слой всего приложения за секунды;</li><li>Получаем автоматическое строгое создание интерфейсов и типов;</li><li>Избавляемся от опечаток в URL и параметрах;</li><li>Обнаруживаем проблемы с обратной совместимостью контрактов на ранних этапах сборки;</li><li>Экономим время.</li></ul><p>Минусы:</p><ul><li>Качество кодгена зависит от качества описания OpenAPI схемы;</li><li>Если схема не генерируется в автоматическом режиме, актуальности OpenAPI схемы потребуется поддерживать вручную;</li><li>Логику нестандартных запросов придётся описывать вручную.</li></ul><p><b>Напоминание:</b> API-слой сложного проекта невозможно покрыть кодогенерацией на 100% — это нормально. Будьте готовы при помощи методов enhanceEndpoints и injectEndpoints комбинировать когден-эндпойнты с эндпойнтам написанными вручную</p><h2>Заключение</h2><p>Кодген ускоряет разработку и делает доставку изменений с бэкенда более безопасной, однако подход требует качественной спецификации, поэтому если у вас небольшой проект — скорее всего кодген для вас будет лишним усложнением.</p><p>В остальных случаях, используя кодген, вы получите предсказуемый и масштабируемый API-слой, который сэкономит сотни часов разработки и поможет вашей команде сместить фокус c рутинных задач на создание качественной бизнес-логики для вашего веб-приложения.</p><p>Ускоряй работу, автоматизируй рутину, используй лучшие тулзы. Все самые полезные инструменты <a href="https://t.me/+iKEwDxvulHFkZDhi">тут</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Утилита с 30 000 звезд на GitHub: как пет-проект стал тулзой для LinkedIn и властей США</title>
      <link>https://tproger.ru/news/utilita-s-30-000-zvezd-na-github--kak-pet-proekt-stal-tulzoj-dlya-linkedin-i-vlastej-swa</link>
      <comments>https://tproger.ru/news/utilita-s-30-000-zvezd-na-github--kak-pet-proekt-stal-tulzoj-dlya-linkedin-i-vlastej-swa?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/utilita-s-30-000-zvezd-na-github--kak-pet-proekt-stal-tulzoj-dlya-linkedin-i-vlastej-swa</guid>
      <description><![CDATA[<p>Инструмент, начатый как пет-проект в 2004 году, стал инструментом для властей США и LinkedIn: 30 000 звезд, 50 сотрудников и путь от PHP до венчурной устойчивости</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/utilita-s-30-000-zvezd-na-github--kak-pet-proekt-stal-tulzoj-dlya-linkedin-i-vlastej-swa">Утилита с 30 000 звезд на GitHub: как пет-проект стал тулзой для LinkedIn и властей США</a>»</p>]]></description>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Стартапы]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Пет-проект]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 01 May 2025 13:51:06 GMT</pubDate>
      <content:encoded><![CDATA[<p>Бен Хейнс <a href="https://medium.com/@ben_haynes/i-started-an-open-source-project-in-2004-8d38820a7ecd">начал</a> писать свой инструмент в 2004 году — задолго до GitHub, до стартапов и даже до появления у него детей.</p><p>Больше новостей — в нашем тг-канале «<a href="https://t.me/your_tech">Представляешь»</a></p><p>Он просто хотел заменить phpMyAdmin чем-то более безопасным и понятным для клиентов. Сегодня у его проекта — <a href="https://github.com/directus/directus">более 30 000 звезд</a>, клиенты из списка Fortune 500 и команда из 50 человек.</p><h2>10 лет в одиночку</h2><p>Первые 10 лет инструмент был сугубо личным. Без сообщества, без поддержки, без амбиций. Только PHP, MySQL и Бен. Когда-то он просто поставил на стол счетчик звезд с GitHub — и каждая новая звезда ощущалась как маленькая победа.</p><h2>10 000 звезд: первая волна</h2><p>Где-то в 2015 году о проекте начали говорить — в Reddit, Slack, GitHub Explore. К проекту присоединился Рейк ван Зантен, будущий со-основатель.</p><p>Вместе они вычистили кучу спагетти-кода, переписали все с PHP на Node и Backbone на Vue. Тогда же их позвали в Сан-Франциско — крупная компания хотела внедрить проект, но испугалась, что за ним стоит всего два человека без юрлица.</p><h2>20 000 звезд: время становиться бизнесом</h2><p>Хейнс закрыл имевшееся у него агентство и оформил компанию. Поднял $1 млн, собрал команду, запустил облачный сервис.</p><p>Затем, после сотни встреч с инвесторами и обвала венчурного рынка, нашел партнера, который понял ценность OSS. Удалось закрыть $8 млн — и с этого момента встал вопрос устойчивости: как зарабатывать, не предавая open-source?</p><p>Ответ — собственная лицензия с бесплатным использованием для всех, кроме корпораций. Работали даже с Брюсом Перенсом, сооснователем OSI.</p><h2>30 000 звезд: устойчивость</h2><p>Сегодня проект — это компания из 50 человек. Впервые на горизонте — прибыль. Продуктом пользуются правительственные агентства, крупнейшие компании, стартапы.</p><p>И да, недавно они тихо подняли еще $9 млн — на новый архитектурный рефакторинг.</p><p>Самое сложное, по словам Бена — сохранять баланс: открытость, прозрачность, вменяемую монетизацию и доверие сообщества.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как простой криптоконвертер стал основой для Супербота</title>
      <link>https://tproger.ru/articles/evolyuciya-ot-kriptokonvertera-do-superkriptobota</link>
      <comments>https://tproger.ru/articles/evolyuciya-ot-kriptokonvertera-do-superkriptobota?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Robin Gad]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/evolyuciya-ot-kriptokonvertera-do-superkriptobota</guid>
      <description><![CDATA[<p>Как минимальный криптоконвертер за выходные вырос в полноценный проект: разбор этапов итеративной разработки, ошибок и идей для развития pet-проектов.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/evolyuciya-ot-kriptokonvertera-do-superkriptobota">Как простой криптоконвертер стал основой для Супербота</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Bitcoin]]></category>
      <category><![CDATA[Криптовалюты]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 27 Apr 2025 09:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>Иногда pet-проект — это не просто «поиграться с кодом». В этой статье расскажу, как с нуля собрал <a href="https://t.me/Kriptoprice_bbot">криптоконвертер</a>, а потом на его основе начал развивать платформу для экспериментов с криптоAPI, автоматизацией и ботами.</p><h2>Итеративная разработка: почему начал с малого</h2><p>Всё началось с минимального прототипа. Это был криптоконвертер — простой, но функциональный. Постепенно я добавлял новые фичи: что-то по запросам из комьюнити, что-то — просто потому что хотелось попробовать. Параллельно шёл рефакторинг: чем сложнее становился проект, тем больше приходилось возвращаться к архитектуре.</p><p>Почему именно конвертер? Это идеальный “hello world” для криптомира:</p><ul><li>нужно подключить внешний API (в моем случае — CoinGecko)</li><li>есть базовая бизнес-логика</li><li>есть простое взаимодействие с пользователем</li></ul><p>Это не только полезный юзкейс, но и отличная точка входа для чего-то большего.</p><h2>Архитектурные решения v1.0</h2><h3>1. Работа с CoinGecko API</h3><p>Первая реализация была прямолинейной:</p><p>Проблема: Каждый запрос — это сетевой вызов. При 1000 пользователях мы превысим лимиты API.</p><p><b>Решение в v1.1:</b> Добавил кеширование через @lru_cache с TTL=5 минут.</p><h3>2. Упрощённая машина состояний</h3><p>Вместо полноценного FSM использовали словарь:</p><p>Почему не FSM? Для v1.0 важна была скорость реализации. Aiogram 3.x предоставляет отличный FSM, но его настройка требует дополнительного времени.</p><h3>3. Валидация ввода</h3><p>Я решил использовать гибридный подход:</p><ul><li>Кнопки для выбора криптовалюты</li><li>Ручной ввод суммы с regex-валидацией:</li></ul><h2>Боль боли: на какие грабли я наступил</h2><p>Расскажу о проблемах, с которыми столкнулся. Спойлер: они были.</p><h3>Ненадёжность публичных API</h3><p>CoinGecko иногда отвечает 10+ секунд или возвращает 502. Решение:</p><h3>Особенности пользовательского ввода</h3><p>Даже с кнопками пользователи умудрялись:</p><ul><li>Присылать “BTC” вместо “bitcoin”</li><li>Использовать запятые вместо точек</li><li>Вводить отрицательные суммы</li></ul><p>Пришлось добавить нормализацию:</p><h3>Состояния при перезапусках</h3><p>При деплое нового кода все сессии сбрасывались. Временное решение — ручное сохранение в JSON, в планах — переход на Redis.</p><p>Наступив на все грабли, я продумал дальнейший roadmap. Среди приоритетов:</p><ol><li>Система алертов</li><li>Графики через matplotlib</li><li>Portfolio tracker</li></ol><h2>Код и рефакторинг</h2><p>Проект выложен на <a href="https://gitflic.ru/project/system_develop/kripto_bot">GitFlic</a>. Самые спорные места кода:</p><ol><li>Отсутствие DI — сервисы передаются через глобальные переменные</li><li>Смесь sync и async вызовов</li><li>Тесты покрывают только core-логику</li></ol><p>Приветствуются пулл-реквесты, особенно по:</p><ul><li>Улучшению обработки ошибок</li><li>Оптимизации запросов к API</li><li>Рефакторингу хендлеров</li></ul><p>Вы можете проголосовать за дальнейшую ветвь развития в Опроснике на моем <a href="https://t.me/+aZ_0zJdcMro2NjAy">ТГ-канале.</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Какие есть паттерны в React и для чего они нужны: часть 1</title>
      <link>https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1</link>
      <comments>https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Юсуп Изрипов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1</guid>
      <description><![CDATA[<p>В этой части Юсуп Изрипов рассказывает, что такое Container &amp; Presentational Components, Higher-Order Component (HOC) и паттерн Render Props в React и что с ними делать.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kakie-est-patterny-v-react-i-dlya-chego-oni-nuzhny--chast-1">Какие есть паттерны в React и для чего они нужны: часть 1</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Tor]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Redux]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Библиотеки]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Тимлид]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 08 Apr 2025 10:30:04 GMT</pubDate>
      <content:encoded><![CDATA[<p>В мире React термин «паттерн» означает какой-то проверенный подход к решению задачи, а не шаблон проектирования из классической книги. За годы разработки вокруг React сформировались свои распространённые паттерны — способы организовать компоненты и логику так, чтобы код получался понятным, поддерживаемым и переиспользуемым.</p><p>Меня зовут Юсуп Изрипов, я сеньор разработчик в VK. Работаю над продуктами, которыми ежедневно пользуются миллионы человек.</p><p>В этих статьях я расскажу вам о самых популярных паттернах и приведу примеры кода. Мы рассмотрим, когда каждый из них может пригодиться, а также отметим их плюсы и минусы. Поговорим о классических приёмах вроде контейнеров и HOC, эволюции к хукам, также обязательно рассмотрим новые паттерны появившиеся в последних версиях React.</p><h2>Container + Presentational Components</h2><p>На первом месте работы ещё во время разработки на Vue мы с подачи нашего тимлида решили ввести этот паттерн. Глобально у нас были так называемые «умные» и «тупые» компоненты (или как я тактично называл их на демо — визуальные). Как вы наверняка догадались, роль Container у нас исполняли «умные» компоненты, а роль Presentational — «тупые». В чём, собственно, суть этого паттерна? Слышали выражение «разделяй и властвуй»? Паттерн Container &amp; Presentational Components (контейнерные и презентационные компоненты) ровно об этом: он разделяет логику (данные и взаимодействие с ними) и отображение (UI) на разные компоненты.</p><p>Presentational Components отвечают только за то, как что-то выглядит. Они получают данные через props и отображают их, больше ничего. Это, как правило, чистые функциональные компоненты, часто без собственного состояния (ну разве что мелкий UI-стейт типа «раскрыт ли dropdown»). Им всё равно, каким образом взять список пользователей — они просто ожидают условный props.users и отображают его в соответствии с дизайном.</p><p>Container Components, напротив, знают, что показать и откуда это взять, но не занимаются тем, как это отображается. Они содержат в себе всю логику: могут загрузить данные, подписаться на store или контекст, хранить состояние, а рендерят в презентационных компоненты, передавая им готовые данные. Контейнер может вообще не иметь собственного HTML, кроме того, что приходит от дочернего презентационного компонента. Его задача — это взаимодействие с данными.</p><p>Зачем же нужен такой подход? Во-первых, лучшая разделённость ответственности (UI отдельно, данные отдельно) упрощает понимание и поддержку приложения. Во-вторых, улучшается переиспользование: один визуальный компонент вероятно использовать с разными источниками данных через разные контейнеры. Дизайнеры могут менять внешний вид компонента в одном месте, не затрагивая бизнес-логику. И тестировать тоже проще: можно отдельно протестировать логику контейнера (без верстки) и отдельно визуальный компонент (с моком данных).</p><p>В этом примере UserList не содержит никакого стейта, не подписан на store или контекст, лишь отображает список. Ему всё равно, как и где получают пользователей — просто принимает проп users и выводит его. Контейнер UserListContainer же занимается работой с данными: делает fetch, сохраняет результат в useState, и потом рендерит UserList, прокидывая в него данные. Благодаря такому делению компонент UserList легко переиспользовать — хоть для локальных данных, хоть для данных из Redux или context — достаточно написать другой контейнер.</p><p>Конечно, не всегда нужно городить пару компонентов вместо одного. Этот паттерн полезен, когда приложение растёт: вы начинаете замечать, что пропсы идут через несколько уровней просто транзитом, или один компонент слишком перегружен логикой. Тогда вы «вытаскиваете» логику в контейнер, а UI — в презентационный компонент, и код сразу становится чище. Это не обязательное правило, а приём для рефакторинга по мере необходимости.</p><p>Стоит отметить, что с появлением React-хуков граница между логикой и отображением несколько размылась. Теперь можно выносить логику в кастомные хуки и вызывать их прямо внутри компонента, вместо того чтобы создавать отдельный контейнер-класс, как это делали до 2018 года. Тем не менее, принцип «держи логику отдельно от представления» по-прежнему полезен. Даже с хуками можно структурировать код, разделяя функциональность: написать хук useUsersData() для получения пользователей и применять его в разных компонентах (вместо дублирования запроса).</p><p>Плюсы: чёткое разделение обязанностей, возможность переиспользовать и заменять части независимо (UI-компонент можно переиспользовать с разными данными), облегчение тестирования.</p><p>Минусы: появляется больше файлов/компонентов, чем могло бы быть, что может казаться избыточным для мелких случаев. Иногда чрезмерное дробление на «глупые» и «умные» компоненты лишь усложняет структуру, если паттерн применён не к месту. Как говорится, включайте голову — не каждую кнопку нужно выделять в отдельный контейнер.</p><h2>Higher-Order Component (HOC)</h2><p>Когда я впервые услышал термин HOC, он показался мне чем-то из математики. Но на практике всё горадо прозаичнее: HOC — это всего лишь функция, которая принимает React-компонент и возвращает новый компонент, оборачивая исходный дополнительной функциональностью. Проще говоря, HOC — это «обёртка». Мы помещаем один компонент внутрь другого, чтобы на выходе получить расширенную версию переданного в HOC компонента.</p><p>Зачем это может понадобиться? Представим, у нас есть несколько разных компонентов, и всем им нужно что-то общее: например, обработка ошибок или подписка на внешние данные. Можно было бы скопировать этот код в каждый из компонентов, но куда элегантнее написать HOC один раз и применить ко всем. Классический пример — Redux-функция connect: вы пишете export default connect(mapState)(MyComponent), и ваш компонент получает пропсы из глобального стейта.</p><p>connect — как раз и есть HOC, который инъектирует данные из Redux в компонент, не требуя от вас переписывать все под Redux вручную.</p><p>Создать свой HOC тоже несложно. Супер банальный пример — сделаем HOC, который добавляет компоненту стейт счётчика:</p><p>Здесь withCounter — HOC, он возвращает новый функциональный компонент WithCounter, который внутри себя использует useState и передаёт состояние и функцию увеличения внутрь WrappedComponent. В итоге EnhancedButton — это улучшенная версия ClickButton, которая умеет считать клики, даже если исходный ClickButton об этом не знал.</p><p>Плюсы: один HOC может добавить функциональность множеству компонентов сразу — не надо копировать код везде. Логику обновляется в одном месте (внутри HOC) — и все обёрнутые компоненты получают изменения. HOC можно комбинировать: например, обернуть компонент сначала в HOC, добавляющий тему оформления, потом в HOC, добавляющий логирование, и т.д. В итоге получим компонент, обладающий сразу несколькими дополнительными возможностями.</p><p>Минусы: за такую магию мы платим усложнением структуры. Когда компонентов обёрток становится много, React-дерево раздувается, и возникает эффект «матрёшки». В DevTools вы могли видеть что-то вроде: Connect(withRouter(WithTheme(MyComponent))) — разобраться, что к чему, становится сложнее. Дебаг таких цепочек — тоже удовольствие то ещё, приходится пробираться через несколько уровней абстракций. Кроме того, HOC часто прокидывают пропсы во внутренний компонент, что чревато конфликтами имён (нужно следить, чтобы, например, prop.title от HOC не перезаписал пропс title, который вы передали самому компоненту). Ещё нюанс — HOC усложняют типизацию в TypeScript (надо правильно описывать generic для пропсов), но это выходит за рамки нашей темы.</p><p>React-разработчики со временем несколько охладели к HOC. В официальной документации прямо сказано: «компоненты высшего порядка не так часто используются в современном React-коде». Отчасти их вытеснили хуки, тем не менее, HOC никуда не делись: их продолжают применять сторонние библиотеки — тот же Redux, Relay и другие. Да, и в старом проекте вы почти наверняка встретите хотя бы пару HOC. Поэтому понимать этот паттерн стоит. Просто имейте в виду современные альтернативы и используйте HOC там, где это действительно необходимо.</p><h2>Паттерн Render Props</h2><p>Следующий паттерн я бы назвал «перевёрнутый HOC». Render Props — это подход, когда компонент сам не рендерит что-то внутри себя, а принимает функцию (часто через проп render или просто используя детей как функцию) и вызывает её, чтобы получить содержимое. То есть мы передаём компоненту инструкцию, что именно отрендерить, а он сам обеспечивает, когда и с какими данными вызвать эту инструкцию.</p><p>Представьте компонент &lt;MouseTracker&gt; для отслеживания положения курсора. Классически он может хранить x, y в своём состоянии и отрисовывать, скажем, &lt;p&gt;Mouse at (x, y)&lt;/p&gt;. Но что, если мы хотим переиспользовать эту логику уже с другим UI? Паттерн Render Props предлагает сделать компонент &lt;Mouse&gt;, который не определяет жёстко JSX внутри себя, а вызывает функцию, переданную через проп (или children функцию), передавая ей координаты. Эта функция сама решит, что рисовать. Таким образом, &lt;Mouse&gt; инкапсулирует логику (слежение за мышкой), а отображение делегирует наружу.</p><p>Пример: реализуем компонент-утилиту &lt;FilteredList items={...} filter={...}&gt;, который отображает список на основе передаваемого фильтра. Вместо того чтобы жёстко прописывать разметку элемента списка, сделаем его с render проп через children:</p><p>Здесь &lt;FilteredList&gt; знает, как отфильтровать массив (items.filter(filter)), но не знает, как отрисовать каждый элемент. Вместо этого он вызывает функцию, которую мы передали в качестве дочернего элемента (children), для каждого элемента списка. Эта функция возвращает &lt;li&gt; для каждого item. В результате логика фильтрации инкапсулируется внутри FilteredList, а конкретное отображение списка задаётся извне. Мы могли бы так же использовать этот компонент для массива объектов, отрисовывая, например, товары — достаточно передать другую children-функцию.</p><p>Паттерн Render Props здорово повышает гибкость компонентов. Мы можем переиспользовать &lt;FilteredList&gt; для списков чего угодно — чисел, пользователей, товаров — просто изменяя функцию отображения. Другой пример: компонент &lt;Mouse&gt; может предоставлять координаты курсора, а внешний код решит, просто вывести текст, нарисовать по координатам картинку или вызвать какую-то совершенно другую логику — не нужно делать несколько вариаций компонента для каждого кейса.</p><p>Плюсы: Render Props позволяет компоненту-провайдеру (в примере выше FilteredList является провайдером данных) быть максимально универсальным, а конкретную разметку делегировать наружу. Многие библиотеки воспользовались этим паттерном: например, React Router (до версии 6) позволял вместо компонента страницы передать проп render в &lt;Route&gt; — функцию, которая отрисует JSX на основе параметров маршрута. Formik предлагал компонент &lt;Formik&gt; с функцией-ребёнком для рендеринга формы. Downshift (библиотека для автокомплитов) — тоже классический пример паттерна render props.</p><p>Минусы: главное неудобство — излишний шум в JSX. Код с вложенными функциями бывает тяжело читать. В нашем простом примере всё компактно, но представьте, если у вас будет несколько уровней таких компонентов: &lt;Foo&gt;{foo =&gt; ( &lt;Bar&gt;{bar =&gt; ( ... )}&lt;/Bar&gt; )}&lt;/Foo&gt; — легко получить «оберточный ад» из стрелочных функций прямо в разметке. Это значительно затруднит отладку такого кода при возникновении каких-либо проблем. К тому же, каждый раз при рендере создаётся новая функция, что может негативно сказаться на производительности, если таких компонентов много (React конечно оптимизирует функции в пропсах через механизм сравнения, но всё же). Также возникает неявная связь: внешний код должен знать, какие аргументы ожидает функция. TypeScript конечно помогает, но при чтении кода не сразу видно, что children, например, это не просто элемент, а функция.</p><p>Как и HOC, паттерн Render Props сейчас используется реже. Многие задачи, решаемые через него, теперь элегантнее с точки зрения кода решаются хуками, в официальной документации это также отмечено. Но всё же понимать его нужно, потому что легаси-код и некоторые библиотеки всё ещё работают на нём. Если видите, что компонент принимает функцию в виде пропса (чаще всего называется render или передаваётся через детей), знайте — это он, Render Props.</p><p>В следующей части расскажу про хуки и кастомные хуки, а также про Compound Components и Серверные компоненты и Suspense.</p>]]></content:encoded>
    </item>
    <item>
      <title>Что больше всего бесит разработчиков? ТОП-10 раздражающих вещей в коде и не только</title>
      <link>https://tproger.ru/articles/chto-bolwe-vsego-besit-razrabotchikov--top-10-razdrazhayushhih-veshhej-v-kode-i-ne-tolko</link>
      <comments>https://tproger.ru/articles/chto-bolwe-vsego-besit-razrabotchikov--top-10-razdrazhayushhih-veshhej-v-kode-i-ne-tolko?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Татьяна Жукова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/chto-bolwe-vsego-besit-razrabotchikov--top-10-razdrazhayushhih-veshhej-v-kode-i-ne-tolko</guid>
      <description><![CDATA[<p>Менторы Solvery рассказывают, что их больше всего раздражает в разработке и как с этим бороться.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/chto-bolwe-vsego-besit-razrabotchikov--top-10-razdrazhayushhih-veshhej-v-kode-i-ne-tolko">Что больше всего бесит разработчиков? ТОП-10 раздражающих вещей в коде и не только</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Slack]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Agile]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[Дизайн]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 01 Apr 2025 10:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Работа программиста — не только интересные задачи и высокий спрос на рынке, но и куча раздражающих моментов, которые мешают писать код в свое удовольствие. Нереалистичные сроки, постоянные прерывания, вечный технический долг и безумные созвоны — это лишь малая часть проблем, с которыми сталкиваются разработчики.</p><p>Вместе с <a href="https://solvery.io/ru/mentor/howtoartyom?utm_source=article&amp;utm_medium=partner&amp;utm_term=tproger&amp;utm_content=top10_razdrozheniy_razrabotchikov&amp;utm_campaign=artem_getashvili">Артемом Геташвили</a> и <a href="https://solvery.io/ru/mentor/stasha_red?utm_source=article&amp;utm_medium=partner&amp;utm_term=tproger&amp;utm_content=top10_razdrozheniy_razrabotchikov&amp;utm_campaign=anastasiya_redchenkova">Анастасией Редченковой</a>, менторами <a href="https://solvery.io/?utm_source=article&amp;utm_medium=partner&amp;utm_term=tproger&amp;utm_content=top10_razdrozheniy_razrabotchikov&amp;utm_campaign=main_page">Solvery</a>, мы собрали ТОП-10 самых раздражающих вещей, которые сводят программистов с ума.</p><h2>Почему разработчики так часто жалуются?</h2><p>Если вы хотя бы раз общались с программистом, то наверняка слышали жалобы на чужой код, странные требования от заказчиков и бесконечные дедлайны. Кажется, что IT-специалисты всегда чем-то недовольны. Но почему так происходит? Разве работа в IT — это не высокие зарплаты, гибкий график и интересные задачи? Давайте разберемся.</p><h3>Миф о том, что разработчики просто «пишут код»</h3><p>Люди, далекие от программирования, думают, что работа — просто писать код, создавая крутые приложения и сервисы. В реальности же программисты больше времени тратят не на что-то новое, а на исправление чужих багов, разбор криво написанной документации и борьбу с дедлайнами.</p><blockquote>Мы жалуемся не потому, что наша работа сложнее других, а из-за особенностей того, как вообще устроено программирование. Когда мы пишем код, нам приходится держать в голове целую систему взаимосвязанных элементов — это как решать сложную головоломку, где каждая деталь на своём месте. И стоит кому-то отвлечь нас — будь то внезапный созвон или резкий чайка-менеджмент с супер срочной задачей — вся эта хрупкая конструкция рушится. После этого приходится начинать сначала: восстанавливать логику, перебирать мысли и заново входить в поток. Переключение внимания для нас — это всегда шаг назад.</blockquote><p>К тому же результаты труда разработчиков не всегда очевидны. Когда дизайнер показывает макет — все видят, как будет выглядеть продукт. Когда архитектор демонстрирует чертеж — понятно, что именно строится. А код? Для неспециалистов он остается загадкой, из-за чего программисты часто ощущают недооцененность своей работы.</p><p>И, конечно же, баги. Их исправление может занимать часы, дни и даже недели.</p><blockquote>Иногда они не дают спать, есть и нормально жить. Бывают простые баги, которые раскапываешь и чинишь за пару часов, а бывает, что ты на неделю-две уходишь в нее с головой. Если видите в 2 часа ночи где-нибудь человека на кухне с ноутбуком, кофе и красными глазами — это точно программист.</blockquote><h3>Почему раздражение — это нормально? (Но как не довести себя до выгорания?)</h3><p>Работа программиста — постоянное решение проблем. А где проблемы, там и стресс. Однако раздражение само по себе — еще не катастрофа. Оно становится проблемой, только если переходит в хроническое состояние.</p><p>Как понять, что раздражение не угрожает вашему психическому здоровью?</p><h4>Здоровое раздражение:</h4><ul><li>Вы злитесь на конкретную ситуацию, но не на профессию в целом.</li><li>После хорошего отдыха появляется энтузиазм вернуться к работе.</li><li>Проблемы мотивируют найти решение.</li><li>Вечером можете отключиться от рабочих мыслей и заняться своими делами.</li></ul><h4>Выгорание:</h4><ul><li>Постоянная апатия даже к интересным задачам.</li><li>Ощущение бессмысленности работы.</li><li>Отсутствие радости даже после решения сложной технической проблемы.</li><li>Мысли о работе преследуют вас везде: на обеде, во время отдыха или сна.</li><li>По утрам тяжело даже открыть ноутбук и запустить IDE.</li></ul><blockquote>Выгорание сопровождается постоянным чувством усталости, которая не проходит даже после отдыха. Оно проявляется снижением интереса к работе и апатией и требует значительных усилий для восстановления.</blockquote><h2>Реальные причины, которые взрывают разработчикам мозг</h2><p>Вместе с экспертами определили топ-10 вещей, которые выводят из себя даже самых стойких разработчиков.</p><h3>Нереалистичные сроки</h3><p><b>Что бесит?</b></p><p>Менеджеры хотят быстрее, заказчики требуют «еще вчера», а разработчик понимает, что качественный код требует времени. В результате приходится либо жертвовать качеством, либо работать в режиме вечного дедлайна.</p><p><b>Как бороться?</b></p><ul><li>Аргументировать реалистичные оценки сроков.</li><li>Документировать риски и объяснять, почему спешка ухудшает результат.</li><li>Использовать Agile и регулярные переоценки задач.</li></ul><h3>Постоянные прерывания и невозможность сконцентрироваться</h3><p><b>Что бесит?</b></p><p>Программисту нужно погружение в задачу, но его бесконечно отвлекают: сообщения в Slack, срочные митинги, вопросы от коллег. Теряется концентрация, а с ней — и продуктивность.</p><p><b>Как бороться?</b></p><ul><li>Блокировать время для глубокой работы.</li><li>Устанавливать «тихие часы» в команде.</li><li>Использовать методы Pomodoro, ALPEN, GTD и прочие.</li><li>В офисе советую работать в наушниках с шумоподавлением.</li></ul><h3>Технический долг</h3><p><b>Что бесит?</b></p><p>Код пишется в спешке, рефакторинг откладывается «на потом», баги растут, но бизнес требует только новых фич. В итоге код превращается в хаос, а разработчик — в археолога, который копается в чужих ошибках.</p><p><b>Как бороться?</b></p><ul><li>Внедрить правило в команде по обязательному рефакторингу каждый спринт (примерно 10–20% времени будет хватить на улучшение кодовой базы).</li><li>Проводить код-ревью и следить за чистотой кода.</li><li>Объяснять руководству, почему технический долг замедляет разработку.</li></ul><h3>Размытые и постоянно меняющиеся требования</h3><p><b>Что бесит?</b></p><p>Сегодня заказчик хочет одно, завтра другое, а на следующей неделе просит переделать все с нуля. Разработчик пребывает в режиме хаоса и переделывает код бесконечно.</p><p><b>Как бороться?</b></p><ul><li>Требовать четкой документации требований.</li><li>Фиксировать договоренности письменно.</li><li>Использовать прототипы для согласования на ранних этапах.</li></ul><h3>Плохая документация</h3><p><b>Что бесит?</b></p><p>Ты впервые работаешь с проектом, открываешь документацию — а там либо пусто, либо устаревшая информация, либо вообще полный беспорядок. Разбираться приходится наугад.</p><p><b>Как бороться?</b></p><ul><li>Создавать и поддерживать актуальную документацию.</li><li>Следовать стандартам оформления документации.</li><li>Автоматизировать процесс обновления документации (например, Swagger).</li></ul><h3>Культура переработок</h3><p><b>Что бесит?</b></p><p>В IT нередко ожидают, что ты будешь работать сверхурочно, чинить баги в продакшене ночью и быть на связи 24/7. Это ведет к выгоранию и снижению мотивации.</p><p><b>Как бороться?</b></p><ul><li>Четко устанавливать границы рабочего времени.<br /></li><li>Искать компании, где соблюдаются нормы трудового кодекса.</li><li>Говорить «нет» переработкам, если это не форс-мажор.</li></ul><h3>Проблемы с зависимостями в проекте</h3><p><b>Что бесит?</b></p><p>Обновляешь одну библиотеку — и рушится весь проект. Версии зависимостей конфликтуют, а разбираться с этим приходится вручную.</p><p><b>Как бороться?</b></p><ul><li>Фиксировать версии зависимостей.</li><li>Использовать инструменты автоматической проверки совместимости.</li><li>Регулярно обновлять пакеты, чтобы не копить проблемы.</li></ul><h3>Недооцененность работы разработчика</h3><p><b>Что бесит?</b></p><p>Менеджеры и заказчики не понимают, сколько усилий уходит на написание чистого кода. Им кажется, что программисты просто «печатают код» и всё готово.</p><p><b>Как бороться?</b></p><ul><li>Показывать метрики и результаты работы.</li><li>Объяснять технические решения простым языком.</li><li>Искать признание среди коллег и в профессиональном сообществе.</li></ul><h3>Необходимость постоянно учить новые технологии</h3><p><b>Что бесит?</b></p><p>IT развивается очень быстро, и разработчику приходится постоянно осваивать новые языки, фреймворки и инструменты. Это утомительно, особенно когда нововведения не несут реальной пользы.</p><p><b>Как бороться?</b></p><ul><li>Изучать только то, что действительно нужно для работы.</li><li>Оценивать, оправдывает ли новая технология затраты на ее изучение.</li><li>Не поддаваться хайпу и не менять стек только ради трендов.</li></ul><h3>Ошибки, которые сложно отловить (дебаг и баги)</h3><p><b>Что бесит?</b></p><p>Программа падает без понятной причины, баги воспроизводятся только «раз в 1000 запусков», а логи ничего не говорят.</p><p><b>Как бороться?</b></p><ul><li>Использовать системы логирования и мониторинга (например, Sentry, Datadog).</li><li>Разрабатывать с учетом отладочности (например, писать больше тестов).</li><li>Применять методы бэктрекинга, чтобы анализировать ошибки шаг за шагом.</li></ul><p>Разработка — не только творчество, но и борьба с кучей раздражающих факторов. Однако понимание проблем и работа над их устранением помогают сделать жизнь программиста проще.</p><p>Какой из этих пунктов раздражает вас больше всего? Делитесь в комментариях!</p>]]></content:encoded>
    </item>
    <item>
      <title>Эпоха TypeScript: почему JavaScript без строгой типизации умирает? Или нет?</title>
      <link>https://tproger.ru/articles/epoha-typescript--pochemu-javascript-bez-strogoj-tipizacii-umiraet--ili-net-</link>
      <comments>https://tproger.ru/articles/epoha-typescript--pochemu-javascript-bez-strogoj-tipizacii-umiraet--ili-net-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Владислав Устинов]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/epoha-typescript--pochemu-javascript-bez-strogoj-tipizacii-umiraet--ili-net-</guid>
      <description><![CDATA[<p>Представьте: вы запускаете новый функционал на продакшен, всё кажется отлично — но через час приходит сообщение от коллег: «Всё сломалось». Знакомо? Для JavaScript-разработчиков это обычная ситуация. Вместе разберёмся, почему так происходит и как TypeScript может спасти от бессонных ночей с дебаггером.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/epoha-typescript--pochemu-javascript-bez-strogoj-tipizacii-umiraet--ili-net-">Эпоха TypeScript: почему JavaScript без строгой типизации умирает? Или нет?</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Динамическая типизация]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sat, 15 Mar 2025 09:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>JavaScript долгое время был королем веб-разработки, гибким и всепрощающим. Но с ростом сложности проектов его слабая типизация стала больше проблемой, чем преимуществом. TypeScript — строгий, надежный, предсказуемый — набирает популярность, обещая избавить разработчиков от множества ошибок. Так умирает ли JavaScript без типизации, или он по-прежнему незаменим? Давайте разберемся.</p><h2>Проблема динамической типизации</h2><p>Как мы обычно объявляем переменные в JavaScript? Просто пишем let или const, даём имя и присваиваем значение:</p><p>Никаких указаний типа. JavaScript не требует от нас заранее объявлять, что userName — это строка (string), а userAge — число (int). И в этом кроется как сила, так и слабость языка.</p><p>В JavaScript переменная может хранить любое значение: строку, число, объект, функцию — что угодно. Более того, мы можем менять тип переменной прямо в процессе выполнения:</p><p>Это и есть динамическая типизация — система, когда тип переменной определяется не на этапе написания кода, а во время его выполнения, и может свободно меняться.</p><p>Удобно, да? Не надо прописывать int или string, среда сама определит, какой тип у переменной. Только вот иногда такой подход приводит к куче ошибок, особенно когда мы имеем дело с большим проектом.</p><p>Разберём на примере функции, которая рассчитывает итоговую стоимость товаров:</p><p>В этом примере мы получим 500300 вместо 800. Дело в том, что мы данные здесь в виде строки, а не числа. При этом во время выполнения JS решил, что мы хотим провести конкатенацию строк, и склеил числа.</p><p>Такие ошибки периодически встречаются, например, если пользователь вводит некорректные данные в форму, либо в коде забыли про преобразование ввода в нужный тип данных.</p><p>В <a href="https://dl.acm.org/doi/10.1145/3159450.3160586">статье</a> «Python Versus C++: An Analysis of Student Struggle on Small Coding Exercises in Introductory Programming Courses» проанализировали успехи студентов в решении задач по программированию на Python и C++. Студенты, которые работали с Python, чаще сталкивались с трудностями при решении задач (23% против 13% у C++) и это при том, что задачи были одинаковые. Python — язык с такой же динамической типизацией данных, как и JS. Большинство трудностей студентов были связаны с ошибками типизации. Вывод: динамическая типизация увеличивает количество ошибок.</p><h3>Критичность ошибки с типами в крупных проектах</h3><p>Теперь представьте, что мы работаем над системой рекомендаций в каком-нибудь крупном сервисе по типу Netflix. У нас десятки тысяч строк кода, тысячи переменных, и нам непонятно, какого они типа.</p><p>В коде таких проектов очень сложно ориентироваться, особенно новым членам команды. Мы не можем с ходу понять, какие типы принимают переменные и функции, никогда не знаешь, где вылезет ошибка.</p><p>А если это банковское приложение и команда прохлопала ошибку с типизацией? Ведь IDE её не подсветит, да и отладка может проигнорировать проблему с типами. Тогда из-за обидного бага компания каждую минуту будет терять бешеные деньги.</p><h2>Инструменты JS для борьбы с ошибками типов и их недостатки</h2><p>Не поймите неправильно, динамическая типизация — это удобно. Мы можем быстро разрабатывать прототипы, небольшие проекты и функции. Но в крупных проектах, таких как системы e-commerce или банковские приложения, где кодовая база превышает десятки тысяч строк, она может стать источником скрытых ошибок.</p><p>В JS есть инструменты, которые нивелируют эту проблему, но они тоже со своими нюансами:</p><p>JSDoc — аннотации типов прямо в комментариях, которые подсказывают IDE (например, VS Code) и разработчикам, что ожидается в коде:</p><p>Плюс: даёт автодополнение и базовую проверку типов в редакторе. Минус: это не часть языка, а просто подсказки — JS сам по себе их не проверяет на этапе выполнения или сборки.</p><p>'use strict' — включает строгий режим, который ловит некоторые ошибки (например, использование необъявленных переменных):</p><p>Плюс: убирает часть «опасных» особенностей JS. Минус: не влияет на типизацию напрямую, а только на синтаксис и поведение.</p><p><b>Ручная проверка в applyDiscount</b>. Мы можем добавлять свои проверки типов:</p><p>Плюс: полный контроль над поведением. Минус: это ручной труд, придется писать проверку для каждой функции, и код не сможет масштабироваться.</p><p>Эти инструменты решают часть проблем динамической типизации, но есть кое-что, что справляется с этой задачей лучше.</p><h2>Что такое TypeScript и как он решает проблему типизации</h2><h3>Строгая статическая типизация</h3><p>TypeScript — язык программирования от Microsoft со строгой статической типизацией. Это значит, что перед объявлением переменной мы заранее определяем тип данных. Например, вот так выглядит переменная строки в JS и TS:</p><p>Мы добавляем к переменной tsString тип данных — string, тем самым, явно указываем, что наша переменная — это строка. Благодаря этому среда понимает тип данных до выполнения кода и распознаёт больше потенциальных ошибок.</p><p>При этом код на TypeScript компилируется в JavaScript. Так у нас сохраняются преимущества JS, но со статической типизацией и минимумом ошибок.</p><h3>Более удобная работа в IDE</h3><p>Компилятор TypeScript способен находить больше ошибок, чем JavaScript. Если среда разработки настроена корректно, то он будет находить ошибки на лету, включая проблемы с типами.</p><p>Рассмотрим 2 примера на JS и TS:</p><p>В выводе с JS после отладки мы получим 53, хотя должно быть 8. Из-за ошибки с типизацией переменной b, среда, скорее всего, не подсветит эту проблему, консоль отладки тоже явно на неё не укажет.</p><p>В пример с TS мы увидим ошибку ещё до отладки, сразу после ввода кода:</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-03-12/cc505158-787a-4789-9622-bdd28cd8b9a9.jpg" alt="" /><figcaption>Подсветка ошибок в TypeScript и JavaScript</figcaption></figure><p>Но даже если по каким-то причинам IDE её не подсветит, мы обнаружим проблему при отладке:</p><figure><img src="https://media.tproger.ru/user-uploads/105039/2025-03-12/0b298270-c056-4d3d-91a9-7b206d4a2d1d.jpg" alt="" /><figcaption>Вывод ошибки в отладке TypeScript</figcaption></figure><p>Статическая типизация — одна из сильных сторон TypeScript, которая упрощает поддержку проектов после их выпуска.</p><p>Нам проще избежать ошибок, связанных с опечатками в именах свойств, вызовами методов с null объектами, передачей аргументов неправильного типа, отсутствием обработки возможных состояний данных. Если мы будем делать рефакторинг, то компилятор быстро подсветит места в коде, которые стоит обновить.</p><h3>Лучшее понимание кода разработчиками</h3><p>Также статическая типизация в TypeScript помогает лучше понять код. Мы можем видеть:</p><ul><li>Какие параметры принимает функция и какого они типа.</li><li>Что функция возвращает.</li><li>Какие свойства имеет объект.</li><li>Какие значения может принимать переменная.</li></ul><p>Это удобно при работе с большими проектами, где над одним кодом работают крупные команды.</p><blockquote>TypeScript позволяет писать поддерживаемый и самодокументированный код, если команда разработки будет правильно использовать этот язык — не злоупотреблять any типом, создавать понятные интерфейсы, писать сигнатуру методов и их возвращаемый тип и т. д. В JavaScript это сделать значительно труднее, если не невозможно. Но эти вещи упрощают поддержку и увеличивают скорость отладки приложения.<br /><br />Сложно представить большой и сложный проект без TypeScript — сегодня это стандарт. Он помогает избежать простых, но очень коварных ошибок, например, передача неправильного типа данных в метод — ошибку увидите сразу. В целом, в таких вещах можно отметить быстрый цикл обратной связи, так как компилятор сразу сообщит о проблеме, и код, который мог бы работать неправильно, не дойдёт до продакшена.</blockquote><blockquote>Сначала кажется, что TypeScript увеличивает время разработки и неспроста: приходится писать дополнительный код под описание типов, интерфейсов и так далее. Но программирование на TypeScript — это игра вдолгую: в дальнейшем все преимущества раскрываются. Некоторые ошибки без TypeScript неочевидны, и могут обнаружиться уже на этапе продакшена в флоу с реальным пользователем, или в лучшем случае на этапе тестирования.</blockquote><blockquote>На одном проекте в моей практике было стандартное правило, которое принимают разработчики, если серьезно относятся к типизации на Typescript. Это запрет на использование типа any. При этом, благодаря линтеру на этапе сборки коммита типа any в коде не было, приходилось все подробно типизировать. В процессе приходили новые разработчики и видели, что мы используем Typescript и все подробно описано, поэтому вопросов по коду у них почти не было.</blockquote><blockquote>Одним из ярких примеров стало внедрение TypeScript в разработку нашего фронтенда. Ранее в JavaScript у нас часто возникали проблемы, связанные с неправильными типами данных при передаче между модулями, особенно когда один разработчик писал код, а другой его использовал. В TypeScript строгая типизация и контракты между модулями помогли нам избежать многих таких ошибок. Еще один пример — API-интеграции. В JavaScript легко передать не тот объект или пропустить обязательное поле, что приводит к неожиданным багам в продакшене. С TypeScript мы смогли использовать интерфейсы и автоматические проверки.</blockquote><h2>Совместимость TypeScript с экосистемой JS</h2><p>Многие популярные библиотеки и фреймворки поддерживают TypeScript. Например, можно смело кодить в React, Angular, Vue. Любой валидный JS код, в большинстве случаев, валиден и для TypeScript. Достигается это за счёт следующих «фишек»:</p><p>Тип any. Это универсальный тип данных, он отключает проверку типов в TypeScript. С одной стороны, этот подход немного неудобный, так как лишает разработчиков преимуществ статической типизации. С другой стороны, бывают случаи, когда это необходимо.</p><p><i>В этом примере функция берёт данные с сайта (API) и говорит TypeScript: «Не проверяй, что внутри, я сам разберусь с помощью any</i><i>»</i><i>. Потом мы пишем data.user.name, и TS не ругается, даже если там нет user или name.</i></p><p>С одной стороны, any — это «костыль», который сводит на нет смысл TS, но с другой — он бывает полезен, например, при работе с плохо типизированными API.</p><p><b>Пакеты @types.</b> Допустим, нам надо импортировать JS библиотеку в TypeScript код. Но эта библиотека не поддерживает TypeScript из коробки. Вариант с типом any нам не подходит, так как мы можем получить много ошибок. В таких случаях подойдёт @types.</p><p>Это обычные npm-пакеты из <a href="https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types">DefinitelyTyped</a>. С их помощью мы получаем доступ к репозиторию, в котором хранятся файлы типов (.d.ts). В них прописаны типы данных для объектов, переменных и иных структур JS библиотек. С помощью этих файлов наш TS код сможет «переварить» JavaScript без ущерба для типизации.</p><p>На февраль 2025 года в DefinitelyTyped доступны типы для более чем 8 тыс. библиотек, что покрывает подавляющее большинство популярных npm-пакетов.</p><p>Пример установки пакета для библиотеки Lodash:</p><p><b>Файлы типов (.d.ts)</b>. Если мы используем какую-то старую JS библиотеку, она не поддерживает TypeScript и для неё нет файлов типов (.d.ts), мы можем прописать их вручную. Пример:</p><p><i>Мы сделали файл .d.ts, чтобы рассказать TypeScript, что старая библиотека old-lib имеет функцию doMagic, которая берёт строку и возвращает число. Теперь, когда мы пишем doMagic('test'), TS понимает, что результат — это число, и не даёт нам случайно использовать его как строку.</i></p><p>Файлы типов (.d.ts) не добавляют исполняемый код, а лишь описывают структуру библиотеки для компилятора TypeScript. Они как переводчик с русского на английский, который подсказывает правильные артикли.</p><blockquote>TypeScript создавался с прицелом на полную совместимость с JavaScript, так что серьёзных проблем обычно не бывает. За последние пару лет я сталкивался с трудностями только в тех случаях, когда использовались старые библиотеки без типизации. Например, однажды пришлось подключить устаревший пакет, у которого не было деклараций типов. Мы решили это, написав минимальный `.d.ts` файл вручную — заняло полчаса. Для современных и популярных библиотек таких проблем не бывает — как правило, у всех есть поддержка TypeScript из коробки.</blockquote><h2>Недостатки TypeScript</h2><h3>Замедляет разработку</h3><p>Когда мы пишем код на TypeScript, то вынуждены явно прописывать типы и исправлять большее количество ошибок, чем при разработке на чистом JS. Это замедляет процесс разработки.</p><p>Также не у всех JS библиотек есть встроенные типы TypeScript, иногда их приходится прописывать вручную, поэтому мы тратим время.</p><p>Справедливости ради, этот недостаток окупается со временем, потому что у нас будет меньше критических ошибок при поддержке проекта и внесении новых изменений.</p><h3>Избыточен для небольших проектов</h3><p>Преимущества TypeScript актуальны для крупных продуктов, где работает команда разработчиков и много запутанного кода. Если мы пишем какой-то свой небольшой проект, например, одностраничный сайт, то у нас будет не так много объектов, классов, переменных и сложных структур. Можно написать это всё на TypeScript, чтобы упростить поддержку, но это замедлит разработку, плюс здесь можно обойтись и стандартными инструментами JS.</p><h3>Сложная кривая обучения для новичков</h3><p>TypeScript хоть и надстройка над JavaScript, но у него другой синтаксис. Здесь мы сталкиваемся со строгой типизацией, интерфейсами, обобщениями, модификаторами доступа и прочими страшными абстракциями.</p><p>Эпоха TypeScript не означает конец JavaScript — скорее, это новый этап в его эволюции. Динамическая типизация хороша для старта, но в сложных системах без строгих типов не обойтись. TypeScript становится всё популярнее, и это не просто хайп: он реально помогает избегать ошибок и ускоряет разработку в долгосрочной перспективе. Его точно стоит добавить в свой стек.</p>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft ускорила TypeScript в 10 раз. Как компании это удалось?</title>
      <link>https://tproger.ru/news/--microsoft-uskorila-typescript-v-10-raz--kak-kompanii-eto-udalos-</link>
      <comments>https://tproger.ru/news/--microsoft-uskorila-typescript-v-10-raz--kak-kompanii-eto-udalos-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--microsoft-uskorila-typescript-v-10-raz--kak-kompanii-eto-udalos-</guid>
      <description><![CDATA[<p>Microsoft ускорила TypeScript в 10 раз, перейдя на нативный компилятор. Компиляция быстрее, память расходуется меньше, релиз — в 2025 году</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--microsoft-uskorila-typescript-v-10-raz--kak-kompanii-eto-udalos-">Microsoft ускорила TypeScript в 10 раз. Как компании это удалось?</a>»</p>]]></description>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Microsoft]]></category>
      <category><![CDATA[TypeScript]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 12 Mar 2025 05:18:57 GMT</pubDate>
      <content:encoded><![CDATA[<p>Microsoft <a href="https://devblogs.microsoft.com/typescript/typescript-native-port/">объявила</a> о <b>радикальном ускорении TypeScript</b>: новая <b>нативная версия компилятора</b> сокращает время сборки <b>в среднем в 10 раз</b>.</p><h2>Как удалось достичь ускорения</h2><p>Microsoft начала <b>портировать TypeScript на нативный код</b>, отходя от текущей JavaScript-реализации. В результате:</p><ul><li><b>Компиляция</b> ускоряется <b>в 9–13 раз</b> (в зависимости от проекта).</li><li><b>Редактор загружается в 8 раз быстрее</b> (на примере Visual Studio Code).</li><li><b>Потребление памяти</b> уменьшилось почти вдвое.</li></ul><p>Вот как изменилось время компиляции в известных проектах:</p><p>Подобные улучшения открывают возможности для <b>мгновенного анализа кода, продвинутых рефакторингов и интеграции ИИ-инструментов</b> в процесс разработки.</p><h2>Редакторы и новые возможности</h2><p>Большинство разработчиков работают в редакторах, где важна скорость. Благодаря нативному компилятору:</p><figure><img src="https://media.tproger.ru/user-uploads/111041/2025-03-12/cb925992-8a14-4723-bde5-44f199739409.jpeg" alt="" /></figure><ul><li><b>Время загрузки VSCode</b> сократилось <b>с 9,6 до 1,2 секунд</b>.</li><li><b>Редакторы станут быстрее</b> за счет перехода на <b>Language Server Protocol (LSP)</b>.</li><li><b>Автодополнение, поиск ссылок, навигация и рефакторинг</b> станут мгновенными.</li></ul><h2>Будущее TypeScript: версия 7.0 и переходный период</h2><ul><li><b>TypeScript 6.x (JS)</b> продолжит получать обновления.</li><li><b>TypeScript 7.0 (нативный)</b> выйдет, когда достигнет полной совместимости.</li><li><b>Обе версии будут поддерживаться</b> параллельно до полного перехода.</li></ul><p>Microsoft обещает регулярно публиковать обновления о проекте.</p><p><b>Первая версия нативного TypeScript для CLI появится уже в середине 2025 года</b>, а полноценный релиз ожидается к концу года.</p>]]></content:encoded>
    </item>
    <item>
      <title>Рефакторинг запросов: как ускорить работу API без переписывания всего кода</title>
      <link>https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda</link>
      <comments>https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda</guid>
      <description><![CDATA[<p>Рефакторинг запросов. Показываем, как ускорить работу API без переписывания всего кода. Рассматриваем пошаговую инструкцию и основные нюансы ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/refaktoring-zaprosov--kak-uskorit-rabotu-api-bez-perepisyvaniya-vsego-koda">Рефакторинг запросов: как ускорить работу API без переписывания всего кода</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Оптимизация]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Redis]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[PostgreSQL]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 24 Feb 2025 14:00:03 GMT</pubDate>
      <content:encoded><![CDATA[<p>API тормозит, а переписывать код с нуля — не вариант? Рефакторинг запросов поможет ускорить работу без радикальных изменений. Разбираем, как оптимизировать API, сокращая задержки и снижая нагрузку на сервер, не разваливая всю систему.</p><h2>Анализируем производительность API</h2><p>Прежде чем ускорять API, нужно понять, что именно замедляет его работу. Для этого оцениваем ключевые метрики:</p><ul><li>Время отклика — сколько времени проходит от запроса до получения ответа.</li><li>Нагрузка на сервер — сколько ресурсов потребляет API при обработке запросов.</li><li>Частота ошибок — как часто сервер возвращает некорректные ответы.</li></ul><p>Отслеживать эти показатели помогают инструменты вроде Postman, New Relic и APM-систем. Они визуализируют данные, автоматизируют тестирование и позволяют находить проблемы в режиме реального времени.</p><h3>Postman</h3><p>Используется для ручного тестирования запросов. Postman показывает время отклика и ошибки. Также через него удобно тестировать сценарии работы API.</p><p>Инструмент удобен тем, что поддерживает коллекции запросов. Это упрощает тестирование сложных цепочек операций во время рефакторинга. Можно интегрировать Newman для автоматизации тестов и анализа метрик на регулярной основе.</p><p><a href="https://tproger.ru/articles/gajd-po-rabote-s-postman">Большой гайд по работе с Postman API Platform</a></p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/bec33c5a-fa3c-4888-af85-885ece3d6f85.jpg" alt="" /><figcaption>Интерфейс Postman</figcaption></figure><h3>New Relic</h3><p>Платформа для мониторинга производительности приложений. Можно отслеживать время обработки API-запросов, статистику по операциям, загрузку системы.</p><p>New Relic показывает распределение времени выполнения между сервером, БД и внешними сервисами, что помогает в рефакторинге разных частей системы.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/732eea24-d64a-4e3b-9585-f2e4f23e2f75.png" alt="" /><figcaption>Интерфейс New Relic</figcaption></figure><h3>APM-системы</h3><p>Инструменты Datadog, AppDynamics или Dynatrace сохраняют данные о том, как работает API. Через них можно смотреть трассировку запросов, выявлять проблемы с медленными вызовами или зависимостями от внешних систем.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/55c2a216-1a7c-46c9-b19a-3324bc9a9493.jpg" alt="" /><figcaption>Трасса в Datadog</figcaption></figure><h3>Ищем узкие места в запросах</h3><p>Для выявления проблемных фрагментов кода необходимо посмотреть, как API реагирует на разные нагрузки, и как распределяется время обработки запроса.</p><p>Поиск состоит из 7 этапов:</p><ul><li><b>Анализ общей картины</b>. С помощью APM-систем и логирования нужно выделить самые медленные запросы.</li><li><b>Локализация длинных операций</b>. В одном запросе может быть несколько узких мест (тяжелый SQL-запрос, внешний API-вызов).</li><li><b>Поиск запросов с высокой частотой вызовов</b>. Например, запросы к слою авторизации или ручки API, к которым обращаются массово при каждом действии пользователя.</li><li><b>Проверка ошибок</b>. Ошибки приводят к дополнительной нагрузке: например, если клиент совершает повторные запросы после тайм-аута.</li><li><b>Анализ распределения нагрузки</b>. Если одни эндпоинты перегружены трафиком, а другие используются редко, в рамках рефакторинга нужно сбалансировать нагрузку. Например, добавить серверы только под обработку горячих эндпоинтов.</li><li><b>Тестирование в условиях пиковой нагрузки</b>. Стресс-тест средствами JMeter поможет выявить проблемы, скрытые в условиях обычного трафика.</li><li><b>Анализ зависимостей API</b>. Замедленная работа внешнего сервиса увеличивает время отклика для каждого запроса. Через Jaeger или Zipkin можно посмотреть цепочку зависимостей.</li><li><b>Локализация длинных операций</b>. В одном запросе может быть несколько узких мест (тяжелый SQL-запрос, внешний API-вызов).</li></ul><h2>Оптимизация запросов к базе данных</h2><p>Уменьшить время отклика API без переписывания кода можно за счёт оптимизации работы с БД. Один из ключевых инструментов — <a href="https://tproger.ru/articles/indeksy-v-postgresql">индексы</a>. Они ускоряют поиск данных, снижая нагрузку на сервер.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/2e25a089-667a-4002-9967-66601f996cfa.jpg" alt="" /><figcaption>B-Tree индекс PostgreSQL</figcaption></figure><p>Что индексировать в первую очередь:</p><ul><li>Поля, используемые в WHERE, JOIN, ORDER BY, GROUP BY. Например, если запросы часто фильтруют по email, имеет смысл добавить индекс.</li></ul><ul><li>Запросы с несколькими условиями фильтрации — их лучше оптимизировать составными индексами. Важно, чтобы порядок полей соответствовал порядку в SQL-запросах.</li></ul><ul><li>Только те данные, где индексация действительно ускорит поиск. Например, индекс для gender (male/female) не даст прироста скорости.</li></ul><p>Лишние индексы замедляют операции записи, их нужно удалять. В PostgreSQL они могут пересекаться, но при дублировании  замедляют запросы. Вместо нескольких отдельных индексов эффективнее использовать составные.</p><h3>Агрегация и выбор только необходимых полей</h3><p>Обработка бесполезных данных увеличивает объем передаваемой информации, замедляет чтение и создает нагрузку на сеть.</p><p>Например, команда SELECT * выбирает все колонки таблицы, включая неиспользуемые. Вместо SELECT * FROM users следует указывать целевые поля: SELECT id, name, email FROM users.</p><p>Если к запросу добавлены ненужные JOIN-ы, группировки или сортировки, в рамках рефакторинга нужно постараться избавиться от них. Избыточные операции на стороне БД рекомендуется переносить в бизнес-логику приложения.</p><p>Снизить объем данных можно за счет COUNT, SUM, AVG, MAX, MIN — эти функции возвращают обобщенные записи вместо отдельных значений. Частичную обработку данных можно выполнять на стороне БД. Например, в PostgreSQL есть встроенные функции по типу JSON_AGG.</p><h3>Пагинация и лимиты в запросах</h3><p>API, работающие со списками пользователей и транзакциями, обязательно должны использовать пагинацию. Лимит на объем возвращаемых записей предотвращает перегрузку серверов и клиента.</p><p>Самое простое решение — во время рефакторинга ограничить количество строк с помощью LIMIT или FETCH FIRST. Например, вместо загрузки всех пользователей SELECT id, name FROM users LIMIT 30 вернет только первые 30 строк.</p><p>Еще можно использовать постраничную выборку (комбинацию LIMIT и OFFSET):</p><p>Если производительность упала, значит офсет слишком большой. В таких случаях лучше перейти на модель курсоров.</p><p>Пагинация с курсорами заменяет OFFSET значением последнего взятого элемента. Например, вместо LIMIT 20 OFFSET 100 можно использовать запрос:</p><p>Чтобы пагинация работала корректно, всегда нужно использовать явный ORDER BY.</p><p><a href="https://tproger.ru/articles/realizuem-paginaciju-v-go-ispolzuja-postgresql">Пагинация на Go в PostgreSQL</a></p><h2>Кэширование</h2><p>Количество запросов к серверу можно сократить, если сохранять часто запрашиваемые данные. Это называется <b>кэшированием</b>. Рефакторинг кэширования уменьшает нагрузку на сервер, снижает время отклика API.</p><p>Рассмотрим инструменты для кэширования.</p><h3>Redis</h3><p>Подходит для серверного и распределенного кэширования. Поддерживает TTL, работу со списками, хэшами и включение репликации для отказоустойчивости. Для интеграции доступны библиотеки и клиенты — ioredis для Node.js или StackExchange.Redis для .NET.</p><p><a href="https://tproger.ru/articles/kak-ispolzovat-redis-dlya-kewirovaniya-i-ocheredej-v-veb-prilozheniyah">Как использовать Redis для кэширования и очередей в веб-приложениях</a></p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/a5b5085d-4c58-45f0-b8f7-b27fcd78ad85.jpg" alt="" /><figcaption>Схема работы Redis, данные хранятся в оперативной памяти сервера</figcaption></figure><p>Рефакторинг кэширования повысит производительность сервиса:</p><ul><li>если API возвращает неизменяемые или редко изменяющиеся данные,</li><li>если часть запросов к БД выполняется кратно дольше остальных.</li></ul><p>Например, в Redis можно сохранять список активных пользователей:</p><h3>Memcached</h3><p>Применяется для уменьшения нагрузки на базу данных и работы с временными данными. Memcached менее функционален, чем Redis, но более эффективен для сценариев, где не требуется сложная логика или постоянное хранилище.</p><p><a href="https://tproger.ru/articles/kak-ispolzovat-servery-redis-i-memcached-dlya-kewirovaniya">Как использовать серверы Redis и Memcached для кэширования</a></p><h3>Виды кэша</h3><p>Выбор типа кэширования зависит от архитектуры API и характера данных.</p><ul><li>Клиентский кэш — данные хранятся на стороне клиента. Например, браузеры загружают страницу дольше в первый раз, но затем мгновенно извлекают её из кэша.</li><li>Серверный кэш — данные сохраняются на сервере или в промежуточном хранилище (Redis, Memcached). Это снижает нагрузку на базу данных: повторные запросы возвращаются из кэша, а не пересчитываются заново.</li><li>Распределённый кэш — данные кэшируются в нескольких узлах для масштабирования и высокой доступности. Например, Redis в режиме кластера помогает организовать кэширование в микросервисной архитектуре.</li></ul><h2>Уменьшение объема передаваемых данных</h2><p>Скорость API зависит от объема данных, передаваемых между клиентом и сервером.</p><h3>Оптимизация формата ответа: JSON vs Protobuf</h3><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/d023e3be-de47-4800-9eed-c034b97920c4.jpg" alt="" /><figcaption>Сравнение JSON и Protobuf</figcaption></figure><p>Формат <b>JSON </b>популярен из-за читаемости и универсальной поддержки большинством языков программирования. Он менее эффективен по сравнению с бинарными форматами. JSON занимает больше места из-за текстового представления структур и значений.</p><p><b>Protobuf </b>(Protocol Buffers) —  бинарный формат, разработанный Google. Он компактен, быстрее сериализуется и занимает меньше места по сравнению с JSON. В формате Protobuf каждый элемент закодирован с минимальным количеством байтов.</p><p>Переход на Protobuf может потребовать изменений в клиентах API, поэтому такая оптимизация выполняется на этапе, когда остальные методы снижения объема данных себя исчерпали.</p><h3>Удаление избыточных данных и компрессия</h3><p>Часто API отправляет больше данных, чем реально нужно клиенту. Это лишний сетевой трафик и задержки:</p><ul><li>Удаление ненужных полей — ограничение выборки данных на стороне сервера: используются выборочные запросы к БД, настройка сериализаторов или фильтрация на уровне представлений.</li><li>Сжатие Gzip — уменьшение размера текстовых данных (JSON, XML) на 70–90%, не требуя изменений в API. Включается на уровне Nginx, Apache или через middleware. Клиенты автоматически распаковывают такие ответы, сохраняя прозрачность процесса.</li></ul><h3>Версионирование API</h3><p>Клиенты обычно нуждаются в данных разного объема. Вместо универсального ответа для всех пользователей рекомендуется разработать несколько версий API.</p><p>Новые версии должны отправлять клиентам упрощенные данные или предлагать другую модель представления без модификации существующих запросов.</p><p>Упрощение достигается через фильтрацию полей на стороне сервера с использованием сериализаторов или форматов ответа. Например, через GraphQL можно сделать так, чтобы клиенты напрямую запрашивали только необходимые поля.</p><p>Чтобы не произошло одновременного отказа у клиентов на старых версиях API, можно временно поддерживать несколько версий. Со временем старую версию нужно объявить устаревшей и отключить.</p><h2>Параллелизация и объединение запросов</h2><p>Оптимизировать API можно за счет рефакторинга batch-запросов. Они объединяют несколько запросов в один — количество обращений между клиентом и сервером сокращается.</p><p>Клиенты отправляют массив запросов, сервер обрабатывает их и возвращает всего один ответ.</p><p>Batch-запросы дают прирост к производительности, когда клиент ожидает получение или обновление нескольких независимых ресурсов.</p><p>Например, мобильное приложение может запрашивать информацию о пользователе и связанных с ним объектах (сообщения, уведомления) за один вызов.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/c893e38f-6031-4a2a-a861-b911798ce759.jpg" alt="" /><figcaption>Схематичное изображение batch-запросов</figcaption></figure><p>Если API поддерживает вложенные запросы, клиент может запрашивать связанные данные одним вызовом. Пример на GraphQL:</p><h3>Асинхронные операции</h3><p>API после рефакторинга будет работать быстрее, если выполнять запросы независимо друг от друга. Сервер может распределять выполнение независимых операций по асинхронным потокам.</p><p>Например, при обработке одного запроса API может обратиться к нескольким подсистемам, отправить синхронные запросы в базу данных и внешние API, параллельно собирая результаты.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/d1a8d43e-6ba2-4c84-866f-caae7301910a.jpg" alt="" /><figcaption>Принцип работы асинхронного API</figcaption></figure><p>Для асинхронных операций часто используются очереди сообщений RabbitMQ или Apache Kafka.</p><h2>Оптимизация на уровне серверной логики</h2><p>Обработку запросов можно откладывать до момента, когда данные действительно понадобятся. Это полезно при работе с запросами, где связанная информация не требуется или используется не полностью. Такое поведение называется <b>lazy loading</b> (ленивая загрузка).</p><p>Загружать связанные данные можно заранее в одном запросе, чтобы сократить количество запросов к БД. Такой принцип работы API называется <b>eager loading</b> (стремительная загрузка). Используется в случаях, когда известно, что все связанные данные понадобятся в ходе операции.</p><figure><img src="https://media.tproger.ru/user-uploads/105601/2025-02-14/56dd35a2-1b7c-437e-b796-cc3ef56b2ea0.jpg" alt="" /></figure><p><b>Lazy loading</b> подходит, если только небольшая связка данных используется в каждом запросе.</p><p><b>Eager loading</b> лучше применять, когда количество запросов к БД критично или весь объем данных необходим для выполнения операции.</p><h3>Очереди для обработки задач в фоне</h3><p>Вместо выполнения тяжелых задач в реальном времени, сервер может добавлять их в очередь, чтобы они обрабатывались фоновыми воркерами.</p><p>Очереди используются для задач, не влияющих на основной ответ клиенту:</p><ul><li>отправка писем,</li><li>формирование отчетов,</li><li>обновление статистики,</li><li>построение индексов,</li><li>обработка данных.</li></ul><p>При поступлении запроса API выполняет только основные операции (валидацию данных или запись в базу), после чего создает задачу в очереди. Процессы выполняются в отдельной среде, не влияя на основной поток обработки запросов.</p><h3>Разделение сложных операций на этапы</h3><p>Комплексные операции, например, генерации отчетов, можно разделить на сбор данных из базы, их обработку и финальное преобразование. Каждый шаг выполняется независимо, а результаты промежуточных операций сохраняются для последующего использования.</p><p>Вместо последовательного выполнения всех шагов, задачу можно поручить системе планировщиков или pipeline в <a href="https://tproger.ru/articles/kak-nastroit-i-ispolzovat-jenkins-dlya-avtomatizacii-processov">Jenkins</a>. Каждый этап становится в очередь и выполняется отдельным процессом.</p><h2>Мониторинг и тестирование</h2><p>После рефакторинга хочется быть уверенным в стабильности API, выявить возможные проблемы и оценить эффективность оптимизаций. Процесс включает:</p><ul><li>нагрузочное тестирование,</li><li>сравнение метрик до и после рефакторинга,</li><li>автоматизацию наблюдения за состоянием API.</li></ul><p>Нагрузочное тестирование определяет, как API справляется с возрастающей нагрузкой. Оно выявляет точки перегрузки. Инструменты для стресс-теста API: Apache JMeter, Gatling.</p><p>На основе результатов нагрузочного тестирования можно проанализировать, насколько рефакторинг улучшил производительность API. В сборе метрик помогут APM-инструменты.</p><p><b>Если показатели значительно улучшились, поздравляем, рефакторинг удался. </b></p><p>Автоматизация мониторинга повышает надежность API и предотвращает проблемы до их масштабного проявления. Инструменты для поддержания производительности в реальном времени: Prometheus + Grafana, Datadog.</p><h2>Что запомнить</h2><ul><li>Производительность API оценивается с помощью метрик: времени отклика, нагрузки на сервер и частоты ошибок.</li><li>Для мониторинга API используются инструменты Postman, New Relic и APM-системы, которые выявляют узкие места системы.</li><li>Оптимизация запросов к базе данных включает индексацию, удаление избыточных операций и выбор только необходимых полей. Для работы с большими объемами данных используется пагинация с применением LIMIT, OFFSET или курсоров.</li><li>Кэширование снижает нагрузку на сервер, сохраняя востребованные данные локально.</li><li>Уменьшение объема передаваемых данных достигается за счет перехода с JSON на Protobuf, компрессии Gzip и фильтрации полей.</li><li>Для сокращения числа запросов к API применяются batch-запросы.</li><li>Асинхронная обработка операций с помощью очередей RabbitMQ или Kafka ускоряет время отклика API для клиентов.</li><li>Очереди для фоновой обработки задач снижают нагрузку на основной поток запросов.</li><li>Нагрузочное тестирование помогает оценить улучшения после рефакторинга.</li><li>Автоматизация мониторинга предотвращает проблемы API до их критического проявления.</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Облачные IDE: Тестируем лучшие онлайн-редакторы кода</title>
      <link>https://tproger.ru/articles/oblachnye-ide--testiruem-luchwie-onlajn-redaktory-koda</link>
      <comments>https://tproger.ru/articles/oblachnye-ide--testiruem-luchwie-onlajn-redaktory-koda?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ростислав Сорокин]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/oblachnye-ide--testiruem-luchwie-onlajn-redaktory-koda</guid>
      <description><![CDATA[<p>Полезные сервисы: Replit, CodeSandbox, GitHub Codespaces, JetBrains Fleet, StackBlitz.
Обзор возможностей, плюсы и минусы, какие задачи лучше решать в каждой среде.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/oblachnye-ide--testiruem-luchwie-onlajn-redaktory-koda">Облачные IDE: Тестируем лучшие онлайн-редакторы кода</a>»</p>]]></description>
      <category><![CDATA[Интернет]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Компиляторы]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[GitHub]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[WebAssembly]]></category>
      <category><![CDATA[JetBrains]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 23 Feb 2025 09:07:32 GMT</pubDate>
      <content:encoded><![CDATA[<p>Когда я впервые столкнулся с облачными IDE, возникло сомнение: «А разве онлайн-среда может заменить привычный локальный редактор, где всё под рукой и настроено?» Однако, спустя несколько лет я убедился, что эти сервисы серьёзно продвинулись в плане функционала и быстроты работы. Сегодня хочу поделиться опытом тестирования пяти популярных решений: Replit, CodeSandbox, GitHub Codespaces, JetBrains Fleet и StackBlitz. Расскажу, в каких случаях они выручат, какие у них плюсы и минусы, а также поделюсь конкретными примерами использования.</p><h2>Что такое облачные IDE и почему они важны</h2><p>Облачные IDE (или онлайн-редакторы кода) позволяют работать над проектом прямо из браузера, без сложной локальной настройки окружения. На сервере уже установлены необходимые инструменты (компиляторы, интерпретаторы), можно быстро переключаться между различными стеками технологий. Это удобно, когда нужно:</p><ol><li>Быстро протестировать идею или прототип,</li><li>Работать из любого места и с любого устройства,</li><li>Обучаться программированию (особенно новичкам, которым сложно настраивать среду разработки),</li><li>Совместно редактировать код в реальном времени.</li></ol><p>Для меня облачные IDE — отличный вариант, когда нужно не заморачиваться с локальной установкой инструментов, а сразу перейти к коду. А ещё они выручают, когда я не хочу загружать ноутбук гигантскими IDE, особенно если машина не слишком мощная.</p><h2>Replit</h2><p><a href="https://replit.com">Replit</a> был одним из первых облачных решений, с которыми я столкнулся. Он ориентирован на скорость старта: заходишь на сайт, создаёшь «репл» (так называются проекты в Replit), выбираешь язык — и готово. Уже предустановлен интерпретатор или компилятор, можно писать и сразу же запускать программу.</p><h3>Возможности</h3><ul><li>Поддержка множества языков: Python, JavaScript, C++, Java, Go, Rust и другие.</li><li>Встроенный чат с искусственным интеллектом (Ghostwriter) для помощи в написании кода.</li><li>Возможность совместной работы: можно дать ссылку коллеге, и он зайдёт в ваш проект, чтобы вместе отлаживать или рефакторить код.</li><li>Хостинг: Replit умеет публиковать веб-приложения «на живом URL» одним нажатием кнопки.</li></ul><h3>Плюсы</h3><ul><li>Очень простая регистрация и моментальный старт.</li><li>Социальные функции: можно смотреть публичные проекты других пользователей, форкать их и учиться.</li></ul><h3>Минусы</h3><ul><li>Не всегда удобен для серьёзных проектов: базовые настройки окружения ограничены, а для расширенных сценариев нужен платный тариф.</li><li>Интерфейс может работать медленнее, если проект становится большим или инсталляция зависимостей тяжёлая.</li></ul><h3>Где пригодится</h3><ul><li>Отличный вариант для новичков, которые хотят учиться программировать без сложной настройки локальной среды.</li><li>Быстрые прототипы, демонстрации кода на собеседованиях или парное программирование.</li></ul><h2>CodeSandbox</h2><p><a href="https://codesandbox.io">CodeSandbox </a>ориентирован, прежде всего, на фронтенд-разработчиков. Он замечательно подходит для проектов на React, Vue, Angular, Svelte и других библиотечных и фреймворковых решениях.</p><h3>Возможности</h3><ul><li>Автоматическое создание окружения для фронтенд-проектов: нужен React? Выбираем шаблон — и сразу получаем структуру, файлы конфигурации и пакетный менеджер.</li><li>«Live preview» (живая перезагрузка): при каждом изменении в коде страница в правой части обновляется без перезагрузки.</li><li>Интеграция с GitHub: можно подтянуть репозиторий, внести правки в CodeSandbox, а затем запушить изменения обратно.</li><li>Возможность запускать бэкенд-сервер (через Docker-контейнеры) в отдельных sandboxes.</li></ul><h3>Плюсы</h3><ul><li>Удобно для веб-разработки: всё уже настроено (Webpack, Vite или другие сборщики).</li><li>Поддержка TypeScript из коробки.</li><li>Отлично работает совместное редактирование, есть возможность прямого «шеринга» результатов.</li></ul><h3>Минусы</h3><ul><li>Фокус именно на веб-технологиях, так что для языков, не связанных с фронтендом, CodeSandbox не всегда подойдёт.</li><li>Бесплатные sandboxes могут засыпать при простое, что не всегда удобно для постоянного хостинга.</li></ul><h3>Где пригодится</h3><ul><li>Быстрые демки UI-компонентов или обучающие примеры для фронтенда.</li><li>Когда нужно показать коллегам, как работает тот или иной React-хук, без установки Node.js локально.</li><li>Создание небольшого прототипа веб-приложения в режиме реального времени.</li></ul><h2>GitHub Codespaces</h2><p><a href="https://github.com/features/codespaces">GitHub Codespaces</a> — по сути, облачное продолжение Visual Studio Code. Если у вас есть репозиторий на GitHub, вы можете открыть его в Codespaces и получить преднастроенную среду разработки прямо в браузере. Это решение особенно нравится разработчикам, которые уже привыкли к VS Code.</p><h3>Возможности</h3><ul><li>Полноценная интеграция с GitHub: открываем репозиторий, создаём Codespace, и всё готово к работе.</li><li>Расширения VS Code в облаке: многие привычные плагины можно установить и использовать.</li><li>Поддержка Docker и Dev Containers: можно создавать контейнеры со своим набором инструментов, чтобы каждый член команды имел идентичную среду.</li><li>Возможность «подцепиться» к Codespaces и локальным VS Code, если хочется работать в офлайне или со своим привычным окружением.</li></ul><h3>Плюсы</h3><ul><li>Знакомая среда для тех, кто пользуется VS Code.</li><li>Гибкий вариант конфигурации (Dev Containers), позволяющий упростить онбординг новых сотрудников.</li><li>Нет проблем с зависимостями: всё хранится в контейнере, поэтому переходить между проектами очень удобно.</li></ul><h3>Минусы</h3><ul><li>Требует платного тарифного плана GitHub, если нужен серьёзный объём ресурсов (хотя есть ограниченное бесплатное время).</li><li>Для маленьких проектов может быть избыточен, так как мощь Codespaces раскрывается больше в средних и крупных командах.</li></ul><h3>Где пригодится</h3><ul><li>Команды, которые живут в экосистеме GitHub, хотят, чтобы любой разработчик мог моментально начать работать с проектом, не тратя часы на установку зависимостей.</li><li>Сложные проекты, где важно гарантировать одинаковую среду для всех.</li></ul><h2>JetBrains Fleet</h2><p><a href="https://www.jetbrains.com/fleet">Fleet </a>— новый проект от JetBrains, который позиционируется как «умная и быстрая» IDE нового поколения. Предлагается как облачная среда с возможностью локальной установки. JetBrains известны своими тяжёловесными, но очень функциональными IDE (IDEA, PyCharm, WebStorm и т.д.), а Fleet стремится занять более лёгкую нишу с возможностью работать в облаке.</p><h3>Возможности</h3><ul><li>Режим «смарт-редактирования», когда Fleet по мере необходимости подгружает функции анализа кода.</li><li>Распределённая архитектура: можно разворачивать часть Fleet в локальном окружении, часть — в удалённом, чтобы снизить нагрузку на машину.</li><li>Совместное редактирование кода в реальном времени, причём JetBrains обещают, что это будет «ровно, как в Google Docs, только для кода».</li></ul><h3>Плюсы</h3><ul><li>Потенциально более лёгкая, чем классические JetBrains IDE, работает быстрее на слабом железе.</li><li>Глубокий анализ кода, как и во всех решениях JetBrains, что особенно полезно для больших проектов.</li></ul><h3>Минусы</h3><ul><li>Fleet пока ещё развивается, некоторые функции отсутствуют или реализованы не до конца.</li><li>Менее дружелюбен к новичкам, чем Replit или CodeSandbox, потому что ориентирован скорее на опытных разработчиков.</li></ul><h3>Где пригодится</h3><ul><li>Разработка на языках, где особенно важны рефакторинг и интеллектуальные подсказки: Java, Kotlin, Python и т.д.</li><li>Если нужна более «лёгкая» альтернатива классическим IDE JetBrains, но при этом с возможностью работать удалённо.</li></ul><h2>StackBlitz</h2><p><a href="https://stackblitz.com">StackBlitz</a> — ещё один «фронтенд-ориентированный» онлайн-редактор, но с упором на выполнение кода прямо в браузере без специальных серверных окружений. Он запускает Node.js-среду в браузере через WebAssembly, благодаря чему проекты могут работать очень быстро и автономно.</p><h3>Возможности</h3><ul><li>Мгновенный запуск Angular, React, Vue, Svelte и т.д., без серверной компоненты.</li><li>Поддержка Node.js-приложений (через WebContainers) прямо в браузере: по сути, локальная VM работает «внутри» вкладки.</li><li>Отличная интеграция с GitHub и возможность деплоя проектов.</li></ul><h3>Плюсы</h3><ul><li>Реально быстрая загрузка и мгновенный старт проектов на популярных фреймворках.</li><li>Почти не зависит от «серверной» стороны, потому что всё работает через WebContainers.</li><li>Хорошо подходит для офлайн-режима (в разумных пределах).</li></ul><h3>Минусы</h3><ul><li>Частично функционал ограничен: если нужны какие-то специфические системные зависимости, WebContainers могут не справиться.</li><li>Не так универсален, как решения уровня GitHub Codespaces. В первую очередь он предназначен для веба.</li></ul><h3>Где пригодится</h3><ul><li>Обучающие примеры фронтенда, демо на конференциях или мастер-классах.</li><li>Когда нужно показать, как работает Angular-компонент или React-хук, а интернет-подключение не самое надёжное.</li><li>Быстрое создание прототипа и проверка идей.</li></ul><h2>Мой личный опыт и выводы</h2><p>За 10 лет работы программистом я привык, что «настоящая IDE» должна быть у меня локально, с тщательно настроенными плагинами. Однако чем дальше, тем больше я использую облачные решения. В одних случаях это экономит время на настройке окружения, в других — позволяет быстро работать даже на слабом устройстве, а в третьих — просто удобно для совместной разработки.</p><ul><li>Replit я использую, когда хочу быстро показать фрагмент кода на собеседовании или протестировать идею на маломальном языке, где нет желания ставить компилятор локально.</li><li>CodeSandbox прекрасно подходит для демонстрации и прототипирования фронтенд-проектов. Идеально, если нужно поделиться примером React или Vue-компонента.</li><li>GitHub Codespaces — моё решение для серьёзных проектов, где нужна стабильная облачная среда уровня VS Code, плюс глубокая интеграция с GitHub Actions и репозиториями.</li><li>JetBrains Fleet я «щупаю» как потенциальную легковесную замену IntelliJ IDEA, но с возможностью облачной синхронизации и удалённой мощью серверов JetBrains. Пока рано говорить о полной замене, но направление впечатляет.</li><li>StackBlitz — это «волшебство», когда нужно поднять Node.js в браузере. Очень люблю показывать StackBlitz на воркшопах по фронтенду, потому что всё запускается буквально за секунды.</li></ul><p>Какую из этих сред выбрать — зависит от задач и предпочтений. Если вы профессионал, который «сидит в одной IDE 8 часов в день», возможно, вы пока предпочтёте локальные решения. Но всё чаще облачные сервисы дают такую же мощь и удобство, причём сэкономив кучу времени на конфигурации. В любом случае, всем рекомендую попробовать, хотя бы ради эксперимента. Может оказаться, что облачная IDE для некоторых задач станет вашим новым любимым инструментом.</p>]]></content:encoded>
    </item>
    <item>
      <title>TTM — менеджерское зло или полезная метрика в разработке?</title>
      <link>https://tproger.ru/articles/ttm---menedzherskoe-zlo-ili-poleznaya-metrika-v-razrabotke-</link>
      <comments>https://tproger.ru/articles/ttm---menedzherskoe-zlo-ili-poleznaya-metrika-v-razrabotke-?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Yuri Nedre]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/ttm---menedzherskoe-zlo-ili-poleznaya-metrika-v-razrabotke-</guid>
      <description><![CDATA[<p>Про TTM можно услышать в любой продуктовой команде. Обычно мы слышим это от разных менеджеров, что надо это ускорять, сокращать и вообще это наш главный показатель. Так ли это? Давайте разбираться. 
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/ttm---menedzherskoe-zlo-ili-poleznaya-metrika-v-razrabotke-">TTM — менеджерское зло или полезная метрика в разработке?</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[QA]]></category>
      <category><![CDATA[Аналитика]]></category>
      <category><![CDATA[Бизнес-аналитика]]></category>
      <category><![CDATA[Бизнес]]></category>
      <category><![CDATA[CI/CD]]></category>
      <category><![CDATA[Дизайн интерфейсов и UX]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 19 Jan 2025 07:47:36 GMT</pubDate>
      <content:encoded><![CDATA[<p>Начнем с того, что такое TTM (Time to Market) — это метрика, которая показывает, сколько времени нужно компании, чтобы пройти путь от идеи до запуска готового продукта или новой фичи на рынок. Иными словами, TTM позволяет измерить скорость, с которой ваша команда способна предоставлять пользователям ценные обновления. В разных компаниях эта метрика может включать все весь флоу задачи (от простой идеи до полной раскатки), в каких-то компаниях какие-то стадии проекта (обычно неактивные фазы блокеры и простои) не учитываются при подсчете ТТМ.</p><p>На первый взгляд, TTM кажется простой концепцией: чем меньше времени тратится на разработку, тем быстрее продукт начинает приносить пользу пользователям и доход компании. Однако за этой простотой скрывается множество нюансов, которые делают TTM одной из самых спорных метрик в IT-индустрии.</p><h2>Почему TTM считается злом, выдуманным менеджерами</h2><p>В разработческих кругах нередко можно услышать скептическое отношение к TTM. Эта метрика воспринимается как инструмент давления со стороны менеджмента: “Сократите сроки! Ускорьте процессы! Почему мы до сих пор не выпустили фичу?”</p><h3>Вот основные причины:</h3><ol><li><b>Поверхностное сокращение сроков.</b> Вместо того чтобы разбираться в главных причинах задержек, руководство часто требует банально сократить сроки. Это может привести к снижению качества кода, поскольку в таком случае вы отказываетесь от рефакторинга и уменьшаете время на тестирование, а значит, продукт на выходе будет куча багов. Вы можете даже пропустить важные стадии проектирования, и в результате получаете непродуманный продукт.</li><li><b>Выгорание сотрудников. </b>Постоянное давление со стороны менеджмента заставляет команды работать в условиях жестких дедлайнов. Это может вылиться в хронический стресс и выгорание, а потом — в низкий уровень вовлеченности и отсутствие мотивации. Следовательно, высокая текучка кадров и дальнейшее усугубление проблемы.</li><li><b>Фокус на скорости, а не на ценности</b>. Менеджеры, ориентированные исключительно на TTM, могут забывать о главной цели разработки — создании ценности для юзера. В результате продукт выходит на рынок недоработанным (но я давно не видел полностью доработанных продуктов выходящих на рынок) и не удовлетворяет потребности целевой аудитории. Как следствие, пользовательский опыт у нас ухудшается, что может негативно сказаться на репутации компании.</li><li><b>Конфликты внутри команды.</b> Когда TTM становится основным KPI, команды начинают ощущать постоянное давление. Это часто приводит к разногласиям между менеджерами и разработчиками, снижению доверия к руководству и в итоге к потере «командного духа», поскольку каждый отдел начинает бороться за выполнение своих задач в ущерб общим целям.</li></ol><h3>Типичные примеры, которые я встречал:</h3><ul><li>В одной из компаний менеджмент решил сократить TTM, урезав время на тестирование. Это привело к тому, что после выпуска продукта пришлось устранять множество багов, что в итоге заняло больше времени, чем изначальная доработка.</li><li>В другой ситуации команда была вынуждена работать сверхурочно несколько месяцев подряд, чтобы уложиться в установленные сроки. Итог — массовое увольнение ключевых специалистов.</li></ul><p>Такие примеры показывают, что неправильное использование TTM может нанести компании больше вреда, чем пользы. Вместо того чтобы помогать команде становиться лучше, эта метрика превращается в источник стресса и демотивации.</p><h2>Почему TTM — это простая и полезная метрика</h2><p>Несмотря на критику, TTM — одна из ключевых метрик, которая помогает понять, насколько эффективно команда и процессы компании в целом способны адаптироваться к изменениям рынка и запросам пользователей.</p><h3>Несколько преимуществ правильного использования TTM:</h3><ol><li><b>Объективный показатель скорости.</b> TTM четко показывает, как быстро идеи становятся реальными продуктами. Это позволяет выявить узкие места в процессе разработки (например, слишком долгие этапы технического проектирования, согласования или тестирования) и оценить эффективность внедрённых изменений в рабочие процессы.</li><li><b>Фокус на ценности для пользователя.</b> Чем быстрее новая функциональность будет доступна пользователю, тем быстрее он сможет ею воспользоваться, следовательно, аудитория может закрыть свои потребности. За счет этого мы укрепляем конкурентное преимущества компании и в итоге увеличиваем доход благодаря более быстрому выходу продукта на рынок.</li><li><b>Стимул к оптимизации процессов.</b> TTM помогает выявить и устранить неэффективные процессы. Например, автоматизация тестирования может сократить время на проверку функциональности. А улучшение коммуникации между отделами ускоряет согласование задач.</li><li><b>Поддержка стратегического планирования.</b> Понимание TTM позволяет нам реалистично оценивать сроки вывода продукта на рынок и учитывать временные рамки при планировании рекламных кампаний и запуске связанных инициатив.</li><li>Мотивация для команды. Когда TTM используется как инструмент для улучшения, а не для давления, он помогает командам чувствовать удовлетворение от оперативного выполнения задач и понимать вклад каждого участника в общий успех.</li></ol><h3>Пара успешных примеров применения TTM:</h3><ul><li>Компания внедрила автоматизацию процессов пайплайна CI/CD , что сократило TTM на 30%. Это позволило быстрее тестировать и выпускать обновления.</li><li>В другой компании улучшили взаимодействие между бизнес-аналитиками и разработчиками, сократив время на согласование требований. В результате TTM для новых фич уменьшился на 20%.</li></ul><h3>Баланс между скоростью и качеством</h3><p>Важно помнить, что TTM не должен использоваться в ущерб качеству. Оптимальный подход — сохранять баланс, где скорость разработки сочетается с достаточным временем для тестирования и проектирования, а команда сосредоточена на создании ценности, а не только на соблюдении дедлайнов.</p><h2>Заключение</h2><p>TTM — это метрика с двойным дном. Она может стать как причиной стресса и конфликтов в команде, так и мощным инструментом, который улучшит процессы и ускорит доставку до конечного юзера. Всё зависит от того, как вы её используете. Вместо того чтобы превращать TTM в инструмент давления, стоит рассматривать её как индикатор для анализа и улучшения работы всей команды. Ведь в итоге главное — это не просто быстро доставить продукт на рынок в каком-либо виде, а сделать так, чтобы он действительно решал задачи пользователей и приносил пользу.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как отладить код: советы для начинающих</title>
      <link>https://tproger.ru/articles/kak-otladit-kod--sovety-dlya-nachinayushhih</link>
      <comments>https://tproger.ru/articles/kak-otladit-kod--sovety-dlya-nachinayushhih?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Елизавета Малышева]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-otladit-kod--sovety-dlya-nachinayushhih</guid>
      <description><![CDATA[<p>Как отладить код. Показываем основные варианты отладки кода для начинающих. Рассматриваем пошаговую инструкцию ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-otladit-kod--sovety-dlya-nachinayushhih">Как отладить код: советы для начинающих</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Отладка]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 15 Jan 2025 10:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>В 2017 году Amazon потерял сотни миллионов долларов из-за одной опечатки.</p><p>Сбой длился 4 часа и привёл к сбою в работе Netflix, Airbnb и даже Комиссии по ценным бумагам и биржам США. А виной всему человеческий фактор: сотрудник занимался рутинной отладкой системы, ошибся в команде и отключил сервера, которые отключать было нельзя.</p><p>Да, относиться к отладке кода нужно не менее внимательно, чем к его написанию. Поэтому сегодня мы покажем, как отладить сложный код просто и без паники. Поговорим о ключевых инструментах и дадим советы, которые помогут даже начинающим разработчикам быстро найти проблему.</p><h2>Так отлаживать или дебажить?</h2><p>Дебажить –– англицизм, который значит то же самое. Используйте тот термин, который вам нравится. А мы будем их чередовать.</p><p>Итак, <b>отладка </b>— процесс выявления и исправления ошибок в программном коде.</p><p>Ошибки бывают разные:</p><h3>Синтаксические</h3><p>Синтаксическая ошибка –– это, например, забытая закрывающаяся скобка или пропущенный отступ.</p><p>Здесь синтаксическая ошибка в том, что мы забыли двоеточие в первой строке:</p><p>В Python обязательно нужно ставить двоеточие после объявления функции. Без этого язык не поймет, что за двоеточием следует тело функции. Поэтому программа выполнена не будет.</p><p>Как правильно:</p><h3>Логические</h3><p>Логические ошибки –– неправильная логика программы. Код может выполняться без проблем в синтаксисе и не выбрасывать исключения, но при этом не давать ожидаемого результата. Такие ошибки трудно отследить, потому что Python не сообщает о них явно.</p><p>Допустим, мы пишем программу для проверки чётности:</p><p>Вместо проверки number % 2 == 0 проверяется number % 2 == 1. Это логическая ошибка.</p><h3>Исключения</h3><p>Исключения — ошибки, которые возникают во время выполнения программы. Например, деление на ноль или попытка получить элемент списка по индексу, которого нет.</p><p>Разберём деление на ноль. Это действие вызовет исключение ZeroDivisionError:</p><p>Такие исключения можно обрабатывать, чтобы программа не упала. Используем try-except:</p><p>Итак, каждая ошибка требует своего подхода. Но если знать, как правильно дебажить, вы сможете значительно ускорить процесс и сделать свой код качественным, понятным и надёжным.</p><p>Перейдём к инструкции по отладке.</p><h2>Шаг 1: Анализируем ошибки и логи</h2><p>Перед тем, как отлаживать код, важно понять: что именно пошло не так? Поэтому первый шаг –– анализ.</p><p>Типы ошибок мы уже разобрали, поэтому будет полегче. Иногда их не получается увидеть сразу в коде –– если бы всё было так просто! Тогда на помощь приходят логи, которые предоставляет программа или ваша IDE.</p><h3>Что с ними делать:</h3><ol><li><b>Изучите сообщение об ошибке</b>. Важно понять, где и почему произошёл сбой. Обычно сообщение указывает тип ошибки и место, где она возникла. Мы уже видели такое поведение в предыдущем разделе, когда забыли поставить двоеточие;</li><li><b>Проверьте логи</b>. В логах будут важные данные об ошибке. Обратите внимание на ключевые слова: Error или Exception;</li><li><b>Воспроизведите ошибку</b>. Попробуйте выполнить код несколько раз. Возможно, это был случайный баг, который больше не повторится.</li></ol><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/3a5b921c-e8b2-44e4-95ed-f08fa42c3352.png" alt="" /></figure><h2>Шаг 2: Разделяем задачу на более мелкие части</h2><p>Сломанный код лучше разбить на более мелкие и понятные части. Это поможет вам найти ошибку быстрее, так как вы точно определите, где её точно нет.</p><h3>Как это работает:</h3><p>Если ошибка возникает в сложном фрагменте, то лучше разделить его на более мелкие шаги.</p><p>Пример: Если программа загружает файл, обрабатывает данные и записывает результат, проверьте отдельно каждую функцию:</p><ul><li>Загружается ли файл?</li><li>Обработались ли данные?</li><li>Успешно ли записывается результат?</li></ul><p>Также можно сузить область поиска ошибки. Если задача большая, начните с проверки небольших фрагментов кода, чтобы изолировать проблемный участок. Загоните ошибку в угол.</p><p>Это лучше, чем хаотично бросаться то в одну, то в другую часть кода и искать иголку в стоге сена.</p><h2>Шаг 3: Воссоздаём проблему</h2><p>Репродукция ошибки — самая важная задача. Чтобы понять, как исправить ошибку, вам нужно её увидеть и поймать. Иногда она проявляется только при определенных условиях, а при каких –– неясно.</p><h3>Тут стоит схитрить:</h3><ul><li>Попробуйте воспроизвести ошибку при разных входных данных. Может быть, строка не ломает программу, а число –– да;</li><li>Пользуйтесь инструментами отладки и отслеживайте каждый шаг программы.</li></ul><p>И тут мы подобрались к самому интересному. Пока что мы говорили о том, как решать проблему. Но не упомянули, с помощью чего. Исправляемся.</p><h3>Отладчики и логирование</h3><p>Отладчики встроены в большинство IDE. Например, в PyCharm или Visual Studio есть встроенные отладчики, с помощью которых можно поставить точки останова.</p><p><b>Точка останова (breakpoint)</b> — инструмент, с помощью которого вы можете остановить программу на определенном этапе и изучить её состояние. Например, проверить, какие значения принимают переменные или что именно происходит перед вызовом ошибки.</p><h3>Как это работает:</h3><ol><li>Установите точку останова в нужной строке;</li><li>Запустите программу в режиме отладки;</li><li>Выполнение остановится на этой строке: изучите текущие данные.</li></ol><h3>Разберём на примере Visual Studio:</h3><p>Напишем небольшую функцию, в которой рассчитывается площадь прямоугольника:</p><p>Поставим точки останова на переменных width, height и result:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/5a407a82-deb0-414e-bfe1-d8769478f691.png" alt="" /></figure><p>Перейдём вот в эту панельку:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/334bf52a-3bb1-4e47-b811-5e171235987a.png" alt="" /></figure><p>И запустим дебаггинг:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/1ccba761-1868-409c-809f-e7236ebae904.png" alt="" /></figure><p>После запуска в редакторе появится новая панелька –– для управления отладкой:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/6ebecc8b-39e8-4810-b0c8-17f4ba15d78f.png" alt="" /></figure><p>С помощью неё можно остановить или перезапустить отладку, перейти к следующей точке останова и так далее.</p><p>В панели слева появятся значения, за которыми мы следим:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/dd57b9aa-07ee-4ed5-81f4-b16fdd0a5325.png" alt="" /></figure><p>Здесь вы можете увидеть, чему равна каждая переменная.</p><p>Напишем функцию с ошибкой, которую уже разбирали:</p><p>Мы пытаемся получить элемент, индекс которого не существует. Красная плашка предупреждает, что возникло исключение:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/83ad8cc2-3a9a-4ff4-9b77-893fc85e255d.png" alt="" /></figure><p>List index out of range –– такого индекса в списке нет. Вроде всё понятно, но можно разобраться.</p><p>Сбоку мы видим список colors, его элементы и длину len(). В Python, как и во многих других языках, отсчёт начинается с 0. Если в списке 3 элемента, то индекс последнего –– 2. Поэтому мы и получаем ошибку.</p><p>Помимо точек останова можно следить за стеком вызовов.</p><p><b>Стек вызовов </b>— цепочка вызванных функций. Она показывает, что происходит на каждом шаге программы.</p><p>Это особенно важно при сложных ошибках, скрытых глубоко в коде. Перепишем немного и сделаем несколько программ:</p><p>Ошибка всё ещё на месте, но теперь её появление происходит не сразу в функции main, а глубже.</p><p>И стек вызовов нам показывает, что сначала выполняется main, затем display_favorite_color и уже потом get_favorite_color, где и живёт ошибка:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/5226b4c3-9504-47a5-a679-ebebf8ad5bc9.png" alt="" /></figure><p>Эту проблему можно решить с помощью  try-except. Советуем попробовать самостоятельно🙂.</p><p>И последний способ отладки, который разберём –– <b>логирование</b>. Можно использовать print() или модуль, который уже заботливо написали за нас. Результат одинаковый — вывод нужных данных в определённое время. Так мы поймём, что лежит в переменной и почему программа ломается.</p><p>Примеры с print мы уже разбирали в предыдущих шагах. Метод простой и лёгкий –– то, что нужно для новичков.</p><p>Разберём вариант посложнее с модулем logging. Его нужно импортировать и настроить:</p><p>Теперь подробнее про конфигурацию. В logging есть несколько типов сообщений:</p><ul><li>Отладочные –– DEBUG. Они показывают подробности о значениях переменных или работе отдельных функций;</li><li>Информационные –– INFO. В них можно прописывать статусы выполнения программы;</li><li>Предупреждения –– WARNING. Сообщения с потенциальными проблемами;</li><li>Ошибки –– ERROR и CRITICAL.</li></ul><p>Мы указали, что хотим видеть все сообщения с помощью строки 3. Теперь в консоли будет отображаться все, что нужно: предупреждения, ошибки и обычные сообщения отладки.</p><p>А на следующей строке указываем, в каком формате хотим видеть сообщение. В итоге получаем точное время, тип сообщения и само сообщение:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/578e4185-621c-4213-90a7-8586c7d0d923.png" alt="" /></figure><p>Это намного удобнее, чем прописывать все данные в print вручную.</p><p>Если нам нужны только ворнинги, то меняем конфигурацию:</p><h2>Шаг 4: Тестируем — юнит-тесты и их роль</h2><p>Когда речь идёт об отладке кода, нельзя забывать о тестах. Потому что они помогают выявить ошибки и опасные места на моменте разработки, когда их ещё легко заметить и поправить.</p><p>Юнит-тесты — отличный способ проверять, что каждый блок вашего кода работает как положено.</p><p>Они особенно полезны в отладке сложных программ и приложений, где ошибки могут проявляться только после нескольких шагов или при конкретных входных значениях. Например, при списках.</p><p>Для этого можно использовать pytest или unittest.</p><p>Давайте поработаем с pytest. Напишем в файле main.py очень простую функцию:</p><p>Функция принимает число и возвращает квадрат числа.</p><p>Напишем тест. Для этого нужно установить pytest в свою IDE и создать файлик. Например, test.py:</p><p>Наша гипотеза: 2 в квадрате равно 4. Для того, чтобы её проверить, используем assert.</p><p>Если в терминале запустить команду pytest test.py, то увидим, что всё прошло успешно:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/ea665183-bc93-4960-a0f0-7120409b02b7.png" alt="" /></figure><p>Если же заменить 4 на 5, то тест упадёт. Так как 2 в квадрате –– не 5:</p><figure><img src="https://media.tproger.ru/user-uploads/105990/2025-01-09/73a7653b-74a0-4fe1-9c1d-335290b99332.png" alt="" /></figure><h2>Советы по улучшению процесса отладки</h2><p>Мы разобрались, как дебажить и тестировать код. Напоследок дадим три базовых совета, которые актуальны не только для Python, но и для любого языка программирования.</p><h3>Делите код на небольшие модули</h3><p>Разбивайте программу на маленькие независимые части. Так их легче тестировать и искать ошибки. Например, вместо одной огромной функции лучше написать несколько компактных. Каждая из них будет отвечать за конкретную задачу. Рефакторинг поможет упростить логику и предотвратить дублирование кода.</p><h3>Пишите автоматические тесты</h3><p>Автоматическое тестирование позволяет ловить ошибки на ранних этапах. Вы можете и не увидеть их сразу. Зато они вас видят.</p><p>Поэтому после написания каждой новой функции добавляйте тесты. Это избавит от сюрпризов в будущем, когда изменение одной программы неожиданно ломает другую.</p><h3>Комментируйте код</h3><p>Не забывайте комментировать код. Объяснять, зачем и как работает каждая функция. Комментарии помогут и вам, и вашей команде быстрее разобраться в коде, особенно спустя несколько месяцев.</p><p>Ну и конечно, называйте функции и переменные понятно. Не усложняйте жизнь себе и коллегам. То, что изначально сделано качественно и с умом, с меньшей вероятностью придётся дебажить.</p><p>Но если всё-таки пришлось, следуйте нашей инструкции, и всё обязательно получится.</p>]]></content:encoded>
    </item>
    <item>
      <title>Фреймворки, меняющие игру: выбираем идеальный инструмент для ваших веб-проектов</title>
      <link>https://tproger.ru/articles/obzor-populyarnyh-frejmvorkov-dlya-veb-razrabotki</link>
      <comments>https://tproger.ru/articles/obzor-populyarnyh-frejmvorkov-dlya-veb-razrabotki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Вадим Егорцев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/obzor-populyarnyh-frejmvorkov-dlya-veb-razrabotki</guid>
      <description><![CDATA[<p>Популярные фреймворки для веб-разработки. Показываем основные виды фреймворков. Рассматриваем пошаговую инструкцию по использованию ✔ Tproger</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/obzor-populyarnyh-frejmvorkov-dlya-veb-razrabotki">Фреймворки, меняющие игру: выбираем идеальный инструмент для ваших веб-проектов</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[JavaScript]]></category>
      <category><![CDATA[Java]]></category>
      <category><![CDATA[Производительность]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Ruby]]></category>
      <category><![CDATA[MySQL]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[SQL]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Ruby on Rails]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Базы данных]]></category>
      <category><![CDATA[Node.js]]></category>
      <category><![CDATA[React]]></category>
      <category><![CDATA[Фронтенд]]></category>
      <category><![CDATA[Бэкенд]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Angular]]></category>
      <category><![CDATA[Vue]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 10 Jan 2025 09:00:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Выбор фреймворка влияет на скорость, удобство разработки, производительность, масштабируемость и поддержку приложения. Рассмотрим популярные варианты для веб-разработки фронтенда, бэкенда и фулстека и сравним их между собой.</p><h2>Frontend фреймворки</h2><h3>React</h3><p>Это самый популярный фреймворк для создания веб-приложений. React используется в лендингах, динамических веб-приложениях и даже мобильных приложениях (<a href="https://tproger.ru/articles/your-first-app-in-react-native">React Native</a>).</p><p>UI здесь разбивается на независимые и переиспользуемые блоки. Такие компоненты легко интегрировать, комбинировать в разных частях приложения или между проектами. Фреймворк ускоряет разработку и рефакторинг.</p><p>React обращается к DOM только для обновления изменившихся компонентов. За счет этого повышается производительность приложения.</p><p>Пример простого React компонента:</p><h4>Преимущества фреймворка:</h4><ul><li>Облегчает повторное использование кода и поддержку приложений;</li><li>Имеет множество дополнительных библиотек и инструментов;</li><li>Виртуальный DOM снижает прямые манипуляции с DOM и повышает производительность приложения;</li><li>Фреймворк пользуется популярностью в сообществе. Как результат  —  множество учебников, разборов документации и сторонних библиотек.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Концепции JSX и управления состоянием могут быть сложны для новичков;</li><li>Плохая кроссбраузерная поддержка;</li><li>Требует дополнительных библиотек для маршрутизации и управления состоянием.</li></ul><h3>Vue.js</h3><p>Vue используется для разработки простых лендингов и комплексных веб-приложений. Это гибкое решение, которое можно интегрировать в проекты постепенно.</p><p>Преимущество Vue в его простоте и низком пороге входа. При этом фреймворк сохраняет статус мощного инструмента для разработки сложных веб-приложений.</p><p>Vue автоматически отслеживает зависимости между блоками и обновляет DOM при их изменении, как React.</p><p>Компонентная архитектура Vue позволяет разбивать UI на переиспользуемые части. Разметка, стили и логика разделяются «оболочкой», а это облегчает поддержку и масштабирование приложения.</p><p>Пример простого компонента Vue:</p><p>Экосистема Vue включает множество библиотек и плагинов, расширяющих возможности фреймворка. Например, Vuex для управления состоянием, Vue Router, Nuxt.js и другие.</p><h4>Преимущества фреймворка:</h4><ul><li>Понятный синтаксис и подробная документация;</li><li>Может использоваться как библиотека или цельный фреймворк в зависимости от проекта;</li><li>Есть поддержка серверного рендера;</li><li>Автоматически обновляет интерфейс при изменении данных;</li><li>Имеет небольшой вес файлов.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Меньше ресурсов и сторонних библиотек по сравнению с React и Angular;</li><li>Неполная документация на русском языке.</li></ul><h3>Angular</h3><p>С помощью Angular разрабатывают клиентские части веб-приложений. Этот фреймворк сложнее Vue и React.</p><p>Angular чаще используют для крупных веб-приложений. Например, панели администрирования, системы управления контентом и т. д.</p><p>По аналогии с Vue и React, фреймворк разбивает приложение на независимые, переиспользуемые блоки кода. Компоненты: шаблон; класс, описывающий поведение; стили.</p><p>Освоение Angular требует больше времени и усилий по сравнению с Vue и React из-за архитектуры и специфических концепций. Нужны углубленные знания по внедрению зависимостей, декораторам и модулям.</p><p>Пример простого Angular-компонента:</p><h4>Преимущества фреймворка:</h4><ul><li>Богатый выбор инструментов, шаблонов, которые сокращают сроки реализации сложных приложений;</li><li>Angular CLI расширяется за счет shematics, поэтому его легко доработать под конкретные задачи;</li><li>Есть поддержка TypeScript: статическая типизация улучшает качество кода;</li><li>Встроенная утилита для обновления проекта при переходе на новую версию Angular.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Ангуляр сложнее, чем Вью и Реакт. Его изучение занимает больше времени, если это первый фреймворк в карьере разработчика.</li></ul><h2>Backend фреймворки</h2><h3>Django (Python)</h3><p>Django подходит для широкого спектра приложений — от блогов до высоконагруженных веб-сервисов. Админ-панель фреймворка упрощает управление контентом и пользователями. Django часто используют для разработки новостных сайтов, интернет-магазинов, социальных сетей и образовательных платформ.</p><p>Фреймворк содержит множество инструментов для решения общих задач веб-разработки. «В комплекте» готовые библиотеки для аутентификации пользователей, администрирования контента, работы с формами, маршрутизации URL.</p><p>Django следует архитектурному шаблону Model-View-Controller (MVC). Модели определяют структуру данных, представления обрабатывают логику и взаимодействие с моделями, а шаблоны отвечают за представление данных пользователю.</p><p>Сильная сторона Django — его ORM (Object-Relational Mapping). Интерфейс для работы с базой данных на Python используется для написания сырых SQL-запросов. Django поддерживает PostgreSQL, MySQL, SQLite, Oracle.</p><p>Если ищете универсальный фреймворк для бэкенд-разработки на Python, Django определенно заслуживает вашего внимания.</p><p>Пример модели Django:</p><p>В примере определяем модель Article с полями title, content и published_at. Метод __str__ возвращает строковое представление объекта:</p><p>Представление article_list получает объекты Article из базы данных и передает их в шаблон для отображения.</p><h4>Преимущества фреймворка:</h4><ul><li>Широкий набор инструментов: включает ORM, аутентификацию и админ-панель;</li><li>Фреймворк задаёт структуру проекта — помогает разработчикам понимать, как и где добавлять новую функцию;</li><li>Защищен от SQL-инъекции и подделки межсайтовых запросов;</li><li>Подходит для высоконагруженных приложений.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Django ORM уступает последней SQLAlchemy.</li></ul><h3>Ruby on Rails (Ruby)</h3><p>Rails подходит для быстрого прототипирования и проектов, ориентированных на работу с базами данных. Его используют для SaaS-платформ, CRM-систем, CMS, маркетплейсов и многого другого.</p><p>Rails применяет архитектурный паттерн MVC (Model-View-Controller). Фреймворк хорош с точки зрения бизнес-логики и взаимодействует с базой данных с помощью ActiveRecord ORM.</p><p>Rails поставляется со встроенными инструментами и библиотеками. Например, ActiveRecord для работы с базами данных, ActiveStorage для управления файлами, ActionMailer для отправки email.</p><p>В сообществе Rails огромное количество гемов (библиотек), расширяющих функциональность фреймворка.</p><p>Пример модели Rails (с использованием ActiveRecord):</p><p>Пример контроллера Rails:</p><p>В примере определяем модель Article с валидациями и связями. Контроллер ArticlesController содержит экшены для списка статей, отображения отдельной статьи, создания новой статьи.  Приватный метод article_params используется для фильтрации параметров.</p><h4>Преимущества фреймворка:</h4><ul><li>Продуманная структура, которая продвигает стандарты качества и лучшие практики веб-разработки;</li><li>Active Record ORM упрощает взаимодействие с базой данных;</li><li>Vue, Angular.js и React легко интегрируются с Ruby on Rails.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Ruby on Rails js медленнее Django и Node.js.</li></ul><h3>Node.js с Express (JavaScript)</h3><p>Express — фреймворк Node.js с набором функций для разработки веб-приложений и API. В нем реализован простой интерфейс для HTTP-запросов и маршрутизации.</p><p>Node.js + Express используется для сервисов, серверных приложений (например, чатов или игр).</p><p>Преимущество Node.js и Express состоит в возможности писать на JavaScript как клиентскую, так и серверную сторону. Можно вести разработку на одном языке и делиться кодом с фронтендом и бэкендом.</p><p>Пример простого приложения на Express:</p><h4>Преимущества фреймворка:</h4><ul><li>JavaScript на всем стеке — один язык для фронтенда и бэкенда;</li><li>Имеет минимальные ограничения, можно строить архитектуру как удобно.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Отсутствуют встроенные компоненты, требуются дополнительные настройки для безопасности и работы с базами данных;</li><li>Управление обратными вызовами усложняет код и его поддержку.</li></ul><h2>Full-Stack фреймворки</h2><h3>Laravel (PHP)</h3><p>Laravel — фреймворк для веб-разработки на PHP. Он использует архитектуру MVC: разделяет логику приложения и улучшает организацию кода.</p><p>Laravel выбирают для разработки CMS, SaaS-платформ, электронной коммерции и др. Встроенные библиотеки ускоряют работу.</p><p>В фреймворке реализована:</p><ul><li>Система миграций для управления структурой базы данных;</li><li>Eloquent ORM для работы с данными;</li><li>Система шаблонизации Blade;</li><li>Встроенная поддержка аутентификации и авторизации;</li><li>Очередь заданий и планировщик для выполнения асинхронных задач.</li></ul><p>Пример определения маршрута в Laravel:</p><p>Пример контроллера Laravel:</p><p>Пример модели Eloquent:</p><p>В примерах выше определяем маршрут, который принимает id и возвращает строку с идентификатором. Контроллер UserController методом show получает пользователя из базы данных по id и передает его в представление user.profile. Модель User представляет таблицу пользователей в базе данных и определяет заполняемые атрибуты.</p><h4>Преимущества фреймворка:</h4><ul><li>Шаблонизатор Blade упрощает создание представлений;</li><li>ORM Eloquent облегчает работу с базами данных;</li><li>Есть автоматизация задач с помощью встроенных инструментов и командной строки.</li></ul><h4>Недостатки:</h4><ul><li>Сложности с долгосрочной поддержкой версий.</li></ul><h3>Spring Boot (Java)</h3><p>Spring Boot применяется для разработки микросервисов и облачных приложений. Модульная архитектура используется в независимых сервисах, которые легко масштабировать.</p><p>Фреймворк имеет встроенный контейнер сервлетов (Tomcat, Jetty), чтобы запускать приложение как автономный JAR-файл без развертывания на внешнем сервере.</p><p>Фреймворк также предлагает:</p><ul><li>Автоконфигурацию библиотек (JPA, JDBC);</li><li>Встроенные метрики мониторинга;</li><li>Поддержку внешней конфигурации;</li><li>Интеграцию с базами данных.</li></ul><h4>Преимущества фреймворка:</h4><ul><li>Есть встроенные серверы Tomcat, Jetty и Undertow;</li><li>Облегчает управление зависимостями с помощью стартовых пакетов;</li><li>Оснащен встроенным контейнером сервлетов;</li><li>Автоматическая конфигурация экономит время на старте проекта.</li></ul><h4>Недостатки:</h4><ul><li>Spring Boot создает множество неиспользуемых зависимостей и  увеличивает размер файла развертывания.</li></ul><h3>Meteor (JavaScript)</h3><p>Особенность Meteor в том, что он синхронизирует данные в реальном времени. Фреймворк автоматически обновляет пользовательский интерфейс при изменении данных на сервере. Это возможно благодаря протоколу DDP (Distributed Data Protocol) и реактивным источникам данных, таких как MongoDB.</p><p>На JS + Meteor можно писать код, который работает как на клиенте, так и на сервере. Фреймворк также предоставляет интегрированную систему сборки, которая автоматически объединяет ресурсы приложения.</p><p>Meteor выбирают для разработки приложений, требующих обновления данных в реальном времени (чаты, приложения для совместной работы, панели мониторинга,  игровые проекты). Его реактивная архитектура и синхронизация данных облегчают создание интерактивных и отзывчивых пользовательских интерфейсов.</p><h4>Преимущества фреймворка:</h4><ul><li>Архитектура и синтаксис с понятным кодом упрощают обучение новичков и повышают производительность команды;</li><li>Есть мгновенная синхронизация данных между клиентом и сервером;</li><li>Возможна интеграция базы данных MongoDB.</li></ul><h4>Недостатки фреймворка:</h4><ul><li>Не предлагает широкого функционала для масштабирования и разделения логики на модули;</li><li>Сложности с масштабированием при увеличении числа пользователей из-за обработки данных в реальном времени.</li></ul><p>При выборе фреймворка важно учитывать долгосрочные перспективы проекта. Также следует принимать во внимание доступность обучающих ресурсов и поддержки. Помните, нет идеального решения. Выбор фреймворка зависит от конкретных требований, ресурсов и предпочтений команды.</p>]]></content:encoded>
    </item>
    <item>
      <title>ChatGPT интегрировался с JetBrains IDE, VSCode, Xcode и другими IDE на macOS</title>
      <link>https://tproger.ru/news/chatgpt-integrirovalsya-s-jetbrains-ide--vscode--xcode-i-drugimi-ide-na-macos</link>
      <comments>https://tproger.ru/news/chatgpt-integrirovalsya-s-jetbrains-ide--vscode--xcode-i-drugimi-ide-na-macos?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/chatgpt-integrirovalsya-s-jetbrains-ide--vscode--xcode-i-drugimi-ide-na-macos</guid>
      <description><![CDATA[<p>OpenAI представила обновление ChatGPT для macOS с поддержкой интеграции в Visual Studio Code, JetBrains IDE (IntelliJ IDEA, PyCharm, GoLand) и Xcode</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/chatgpt-integrirovalsya-s-jetbrains-ide--vscode--xcode-i-drugimi-ide-na-macos">ChatGPT интегрировался с JetBrains IDE, VSCode, Xcode и другими IDE на macOS</a>»</p>]]></description>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[iOS]]></category>
      <category><![CDATA[macOS]]></category>
      <category><![CDATA[JetBrains]]></category>
      <category><![CDATA[OpenAI]]></category>
      <category><![CDATA[PyCharm]]></category>
      <category><![CDATA[Приложение]]></category>
      <category><![CDATA[Xcode]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 28 Nov 2024 05:33:06 GMT</pubDate>
      <content:encoded><![CDATA[<p>OpenAI <a href="https://help.openai.com/en/articles/9703738-macos-app-release-notes">представила</a> обновление ChatGPT для macOS, добавив поддержку интеграции с популярными IDE, такими как Visual Studio Code (и её производные), JetBrains IDE, включая IntelliJ IDEA, PyCharm, GoLand, а также Xcode.</p><p>Теперь разработчики могут использовать возможности ChatGPT прямо в своих средах разработки.</p><h2>Какие IDE поддерживаются?</h2><p>Обновление охватывает следующие инструменты:</p><ul><li>Visual Studio Code и форки: поддержка включает помощь с генерацией кода, рефакторингом и разъяснениями сложных частей проекта.</li><li>JetBrains IDE: поддержка распространяется на такие популярные IDE, как IntelliJ IDEA, PyCharm, GoLand и другие. Она расширяет возможности разработчиков, работающих с различными языками программирования.</li><li>Xcode: интеграция позволяет iOS-разработчикам использовать ChatGPT для ускорения процесса написания кода и тестирования.</li></ul><h2>Возможности интеграции</h2><p>Обновление добавляет несколько полезных функций:</p><ul><li>Подсказки и генерация кода: ChatGPT помогает автоматически генерировать шаблоны, подсказки и готовые решения.</li><li>Рефакторинг и улучшение кода: возможность улучшать читаемость и оптимизировать структуру кода.</li><li>Обучение и документация: AI может объяснять сложные части кода и предлагать альтернативные подходы.</li></ul><h2>Зачем это нужно?</h2><p>Свежая возможность позволяет разработчикам экономить время и концентрироваться на решении ключевых задач, вместо того чтобы тратить его на поиски решений.</p><p>Это также удобно для обучения, особенно для тех, кто только начинает работать с новыми технологиями.</p><h2>Доступность</h2><p>Обновление уже доступно для пользователей macOS.</p><p>Чтобы воспользоваться интеграцией, достаточно обновить приложение ChatGPT и настроить плагин для вашей IDE. Инструкции по установке доступны на сайте OpenAI.</p><h2>Реакция сообщества</h2><p>Разработчики уже отмечают значительное повышение продуктивности благодаря прямой интеграции ChatGPT с инструментами разработки. Ожидается, что в будущем OpenAI расширит список поддерживаемых платформ, включая Windows и Linux.</p>]]></content:encoded>
    </item>
    <item>
      <title>Haystack — необычная IDE с визуальным подходом для классического программирования</title>
      <link>https://tproger.ru/news/--haystack---neobychnaya-ide-s-vizualnym-podhodom-dlya-klassicheskogo-programmirovaniya</link>
      <comments>https://tproger.ru/news/--haystack---neobychnaya-ide-s-vizualnym-podhodom-dlya-klassicheskogo-programmirovaniya?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Булат Яббаров]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/news/--haystack---neobychnaya-ide-s-vizualnym-podhodom-dlya-klassicheskogo-programmirovaniya</guid>
      <description><![CDATA[<p>Haystack — новая IDE, которая предлагает визуальный подход к работе с кодовой базой. Она превращает код в визуальный граф на холсте и использует ИИ для автоматизации рутинных задач. Инструмент интегрируется с VSCode, поддерживает сохранение рабочих пространств и ускоряет процессы разработки. </p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/news/--haystack---neobychnaya-ide-s-vizualnym-podhodom-dlya-klassicheskogo-programmirovaniya">Haystack — необычная IDE с визуальным подходом для классического программирования</a>»</p>]]></description>
      <category><![CDATA[Linux]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[Искусственный интеллект]]></category>
      <category><![CDATA[Windows]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[macOS]]></category>
      <category><![CDATA[Визуализация]]></category>
      <category><![CDATA[Новости]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 23 Aug 2024 06:15:19 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчики теперь могут облегчить себе жизнь благодаря новой среде разработки под названием Haystack.</p><p>Этот инструмент был создан специально для тех, кто работает с большой и сложной кодовой базой, предлагая удобные визуальные решения и автоматизацию рутины.</p><h2>Основные особенности и преимущества</h2><h3>Визуализация на холсте</h3><p>Haystack превращает вашу кодовую базу в визуальный граф, который отображается на бесконечном холсте.</p><p>Это позволяет легко перемещаться между функциями, классами и методами, а также видеть взаимосвязи между ними. Такой подход значительно упрощает понимание структуры проекта и ускоряет процессы навигации и рефакторинга.</p><h3>Автоматизация с помощью ИИ</h3><p>Одним из ключевых преимуществ Haystack является навигационный «копилот», который использует искусственный интеллект для автоматизации рутинных задач.</p><p>Этот инструмент предугадывает ваши следующие шаги и предлагает изменения в коде, что позволяет ускорить процесс разработки. Например, Haystack может автоматически создавать методы или вносить изменения в код, которые вам потом нужно будет просто подтвердить или отклонить.</p><h3>Удобство и интеграции</h3><p>Haystack поддерживает интеграцию с популярными инструментами для разработки, такими как VSCode, что делает переход на новый инструмент максимально безболезненным.</p><p>Это значит, что вы можете сохранять свои настройки, расширения и горячие клавиши, продолжая работу в привычной среде, но с новыми возможностями.</p><h3>Многофункциональность</h3><p>Haystack также предлагает уникальные функции, такие как сохранение и загрузка рабочих пространств, что позволяет быстро переключаться между проектами или задачами.</p><p>Этот функционал особенно полезен для тех, кто работает над несколькими проектами одновременно или часто меняет фокус своей работы.</p><h2>Планы на будущее</h2><p>Разработчики Haystack активно работают над расширением функционала, включая поддержку большего количества языков программирования и добавление новых функций для анализа кода.</p><p>В ближайшем будущем планируется интеграция инструментов для анализа производительности кода и поддержки написания сложных сценариев, что сделает Haystack еще более мощным и полезным инструментом для программистов.</p><p><a href="https://haystackeditor.com/">Скачать</a> IDE можно с официального сайта.</p>]]></content:encoded>
    </item>
    <item>
      <title>Платформа для работы с исходным кодом GitVerse получила масштабное обновление</title>
      <link>https://tproger.ru/articles/platforma-dlya-raboty-s-ishodnym-kodom-gitverse-poluchila-maswtabnoe-obnovlenie-erid-ljn8kxjg3</link>
      <comments>https://tproger.ru/articles/platforma-dlya-raboty-s-ishodnym-kodom-gitverse-poluchila-maswtabnoe-obnovlenie-erid-ljn8kxjg3?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ирина Тюльпакова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/platforma-dlya-raboty-s-ishodnym-kodom-gitverse-poluchila-maswtabnoe-obnovlenie-erid-ljn8kxjg3</guid>
      <description><![CDATA[<p>На платформе доступны новые инструменты, ускоряющие разработку, реализован чат в GigaCode, а пользоваться GitVerse теперь может малый и средний бизнес.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/platforma-dlya-raboty-s-ishodnym-kodom-gitverse-poluchila-maswtabnoe-obnovlenie-erid-ljn8kxjg3">Платформа для работы с исходным кодом GitVerse получила масштабное обновление</a>»</p>]]></description>
      <category><![CDATA[API]]></category>
      <category><![CDATA[Open Source]]></category>
      <category><![CDATA[Инструменты]]></category>
      <category><![CDATA[Git]]></category>
      <category><![CDATA[Безопасность]]></category>
      <category><![CDATA[JSON]]></category>
      <category><![CDATA[Ruby]]></category>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[IDE]]></category>
      <category><![CDATA[CSS]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Работа]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[HTML]]></category>
      <category><![CDATA[Боты]]></category>
      <category><![CDATA[Rust]]></category>
      <category><![CDATA[HR]]></category>
      <category><![CDATA[Сервисы]]></category>
      <category><![CDATA[PyCharm]]></category>
      <category><![CDATA[Мессенджер]]></category>
      <category><![CDATA[ARM]]></category>
      <category><![CDATA[Сбер]]></category>
      <category><![CDATA[Сервер]]></category>
      <category><![CDATA[Фулстек-разработка: полный цикл]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 29 Mar 2024 16:20:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Разработчик платформы СберТех (дочерняя компания Сбера) внес десятки улучшений в базовую функциональность платформы для работы с исходным кодом <a href="https://gitverse.ru/promo/announcement-conference?utm_source=sm_tproger&amp;utm_medium=cpm&amp;utm_campaign=sm_202401033103_media_march_2024_rk528559gr6134&amp;utm_content=article&amp;utm_term=event">GitVerse</a> и представил ее новые возможности, которые помогают ускорить и упростить разработку. Об этом команда платформы рассказала на онлайн-презентации «GitVerse: открой вселенную кода».</p><h2>CI/CD-инструменты</h2><p>CI/CD-инструменты позволят автоматизировать сборку исходного кода и процессы поставки. Разработчики теперь могут воспользоваться уже написанными скриптами сборки, в один клик перенося свои проекты с Git-репозиториев. Технология оповещения о новых событиях на сервере (вебхуки) позволяет реализовать еще больше сценариев автоматизации. По событиям в GitVerse можно вызвать через API сторонние сервисы. Например, при определенных событиях в репозитории можно запустить сторонний сборочный конвейер или отправить уведомление в мессенджер.</p><h2>Новые функции персонального AI-ассистента разработчика GigaCode</h2><p>Теперь AI-ассистент поможет разработчику решать задачи, связанные с кодом, в окне чата непосредственно в среде разработки. Сервис чата также доступен и в GitVerse, где при просмотре репозитория можно получить объяснение, что делает та или иная часть кода, а также советы по его улучшению. Список языков программирования, которые поддерживает GigaCode, пополнил Ruby, а также стала доступна генерация текстовых данных в формате JSON. На сегодняшний день AI-ассистент поддерживает уже более 15 популярных языков программирования и устанавливается как плагин в привычные среды разработки, включая IDEA, PyCharm, VSCode, Jupyter.</p><h2>Функциональность для организаций</h2><p>Теперь разработка на GitVerse доступна не только индивидуальным разработчикам, но и малым и средним предприятиям. Компании могут организовать совместную работу команды и управлять доступами к своим репозиториям.</p><blockquote>При разработке новой функциональности мы учитываем пожелания пользователей, добавляем необходимые инструменты, чтобы они могли вести разработку еще проще и быстрее. С каждым релизом платформа будет становиться все более удобной и пополняться новыми популярными репозиториями, open source-версиями продуктов и инструментами для эффективной разработки. Будущее разработки мы видим в создании удобной среды по принципу единого окна, в которой все члены команды могут работать на своем этапе производственного процесса, заказывать облачную инфраструктуру и общаться. На всех этапах разработки партнерскую роль будет занимать AI: помогать писать код, советовать, как сконфигурировать стенд, готовить документацию, подсказывать шаги по CI/CD-конвейеру</blockquote><p>Также на мероприятии представили дорожную карту развития платформы, согласно которой в этом году появится еще больше полезной функциональности для разработчиков:</p><ul><li>новые инструменты для управления проектами, позволяющие удобно организовывать рабочие процессы;</li><li>интегрированная среда разработки позволит разворачивать полностью настроенные инструменты разработки в облаке. Функциональность будет доступна прямо из браузера: разработчик сможет легко и быстро открыть любой репозиторий GitVerse в среде разработки;</li><li>новые функции GigaCode: генерация тестов, автоматическое создание документации и умный рефакторинг. Число языков, которые поддерживает GigaCode, пополнят PHP, HTML, CSS, Markdown и Rust; <b> </b></li><li>инструменты для безопасной разработки (оркестрация CI/CD, статический анализатор, управление секретами и безопасность зависимостей);</li><li>удобный и безопасный вход через популярные сервисы идентификации личности, а также мобильная версия платформы.</li></ul><p><i>Реклама Реклама АО “Сбертех” ИНН 7736632467, </i> <i>LjN8KXJG3</i></p>]]></content:encoded>
    </item>
    <item>
      <title>Анонимные и стрелочные функции: как использовать их вместо create_function в PHP 8</title>
      <link>https://tproger.ru/articles/anonimnye-i-strelochnye-funkcii--kak-ispolzovat-ih-vmesto-create-function-v-php-8</link>
      <comments>https://tproger.ru/articles/anonimnye-i-strelochnye-funkcii--kak-ispolzovat-ih-vmesto-create-function-v-php-8?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Виталий Киреев]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/anonimnye-i-strelochnye-funkcii--kak-ispolzovat-ih-vmesto-create-function-v-php-8</guid>
      <description><![CDATA[<p>Рассказал, как работают анонимные и стрелочные функции в PHP. А также, на что заменить create_function при переходе с PHP 7.4 на PHP 8.
</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/anonimnye-i-strelochnye-funkcii--kak-ispolzovat-ih-vmesto-create-function-v-php-8">Анонимные и стрелочные функции: как использовать их вместо create_function в PHP 8</a>»</p>]]></description>
      <category><![CDATA[PHP]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Legacy]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 03 Mar 2024 12:50:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>PHP выкатывает крупные обновления почти каждый год. В новых версиях появляются фичи, позволяющие увеличить производительность, а иногда частично меняется синтаксис. С выходом обновления две предыдущие версии PHP поддерживаются разработчиками еще три года — они продолжают устранять ошибки и улучшать безопасность. Когда время проходит, за старыми версиями PHP перестают следить, и некоторые функции полностью перестают работать. Если вовремя не обновить код в проекте, начнут появляться ошибки.</p><p>Меня зовут Виталий Киреев, я руководитель отдела исследований и разработок хостинг-провайдера SpaceWeb. В статье расскажу, как мы с нашей командой проводили рефакторинг кода при переходе с PHP 7.4 на PHP 8 и на что заменили одну из самых популярных функций — create_function.</p><p>Статья будет полезна и тем, кто только погружается в язык PHP. На примере реальной задачи по рефакторингу подробно разберу, как использовать анонимные и стрелочные функции. И объясню, чем они отличаются.</p><h2>Контекст: когда стало ясно, что нужен рефакторинг</h2><p>В части проектов SpaceWeb много legacy-кода. Мы поддерживаем проекты с версии PHP 5.6, которая появилась в 2014 году. С некоторой периодичностью переходим на новые версии PHP, постоянно обновляя код.</p><p>Оставлять прежний код в старых версиях PHP — плохая идея, потому что в какой-то момент он может перестать работать. А еще в них могут быть уязвимости в разрезе безопасности. Обновленные версии PHP обычно включают исправления ошибок и новые языковые возможности, которые могут улучшить производительность сайтов и приложений.</p><p>Когда вышла версия PHP 7.4, функция create_function, которая часто использовалась в нашем коде, стала deprecated, то есть перестала поддерживаться и работать. Из-за этого стали появляться ошибки в коде. Тогда и стало ясно, что нужен рефакторинг.</p><p>Подсветить часть проблем в коде нам помогли статические анализаторы кода — PHP Code Style Fixer и PHPstan. Но большую часть кода мы проверяли и обновляли вручную. В первую очередь нужно было придумать, на что заменить  популярную create_function. Сперва начали переход на анонимную функцию и замыкание. А спустя время внедрили и стрелочные функции, чтобы повысить читаемость кода и решить дополнительные задачи.</p><h2>Анонимные функции: как работают и как применить замыкание</h2><p>Особенность анонимных функций в том, что они задают область видимости. Внутри этой области будут работать только те переменные, которые определены там же. Если мы пропишем переменные за пределами функции, код будет выдавать ошибку.</p><p>На самом деле, create_function, которую мы обычно использовали до PHP 8 — это классический пример использования анонимной функции. Ее часто применяли для обработки данных в массиве. Функция содержит два аргумента — массив аргументов функции и сам код.</p><p>Когда эта функция стала deprecated в PHP 8, мы начали рефакторинг кода. Анонимные функции стали выглядеть так:</p><p>Но что, если нам потребуется вызвать эту функцию в нескольких разных местах? Тогда можно использовать замыкание. Это позволит вызывать функцию, используя ее как переменную.</p><p>А как быть, если нам нужно использовать переменные из окружения, и значения переменных  будут меняться в процессе выполнения программы? Здесь нам помогут стрелочные функции.</p><h2>Стрелочные функции: в чем отличие от анонимных и где лучше использовать</h2><p>Стрелочные функции позволяют избежать постоянного использования use, потому что в них есть автоматический захват внешних переменных. Это позволяет сделать код более лаконичным. Еще стрелочные функции лучше подходят для работы с большим количеством переменных или с частой сменой переменных в процессе.</p><p>В этом примере была задача отфильтровать контракты и соотнести их с нужным ИНН. Если бы решали ее с помощью анонимных функций, то под каждый ИНН пришлось бы писать отдельную функцию. А еще везде использовать use.</p><p>С помощью стрелочных функций получилось решить задачу быстрее, потому что они позволяют получить доступ ко всей области видимости. Это уменьшает время на дальнейшую поддержку кода, а доступ к области видимости выше можно получить без дополнительных функций.</p><p>Важно отметить, что у стрелочных функций есть ограничение. Они не могут быть слишком сложными, у них компактный синтаксис. Если нам нужно разместить многострочный код, мы используем анонимные функции.</p><h2>Итоги</h2><p>Для замены create_function в PHP 8 можно использовать как анонимные, так и стрелочные функции.</p><p>Анонимные функции в PHP позволяют создавать функции, не имеющие определенных имен. Они работают в заданной области видимости и только с теми переменными, которые определены там же. Преимущество анонимных функций — в них можно разместить многострочный код, в отличие от стрелочных.</p><p>Стрелочные функции позволяют избежать постоянного использования use и упростить синтаксис. А еще они лучше подходят для работы с большим количеством переменных или с частой сменой в процессе.</p>]]></content:encoded>
    </item>
    <item>
      <title>Легаси: поддерживать нельзя переписать</title>
      <link>https://tproger.ru/articles/legasi--podderzhivat-nelzya-perepisat</link>
      <comments>https://tproger.ru/articles/legasi--podderzhivat-nelzya-perepisat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Ирина Тюльпакова]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/legasi--podderzhivat-nelzya-perepisat</guid>
      <description><![CDATA[<p>Легаси — реальность любого программиста. Объясняем, как софт становится легаси и почему это нормально, а также какие существуют плюсы при работе с легаси.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/legasi--podderzhivat-nelzya-perepisat">Легаси: поддерживать нельзя переписать</a>»</p>]]></description>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Разработка]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 09 Feb 2024 08:17:56 GMT</pubDate>
      <content:encoded><![CDATA[<p>«Легаси» — это слово, которым программисты пугают друг друга (и менеджеров). Оно означает устаревший софт, работать с которым обычно сложно и/или неприятно по причине небольшого «выхлопа» в пересчете на вкладываемые усилия. Обобщив, словом  «легаси» можно назвать любой «код», который сложно поддерживать. И чем сложнее, тем он более «легаси».</p><p>Сегодня расскажем, откуда он берется, как удерживать его «в рамках» и чем он может быть полезен для начинающих специалистов.</p><p>Неважно, как хорошо и чисто написан код — рано или поздно он станет легаси.</p><h2>А что плохого в легаси?</h2><p>Основные недостатки легаси для бизнеса, это:</p><ul><li>низкое соотношение пользы к вложенным усилиям на изменения;</li><li>медленные изменения;</li><li>сложности с подбором команды для работы над кодом.</li></ul><h2>Причины, по которым появляется легаси</h2><p>Причины условно можно разделить на две группы: не зависящие от команды разработки, и те, на которые команда повлиять может.</p><p>К первым можно отнести устаревание технологий, ОС, языков программирования, протоколов, библиотек, фреймворков, да и подходов к проектированию систем (например, при появлении новых возможностей, скажем, многоядерных процессоров). Под устареванием понимается, то, что на смену им приходят новые технологии, позволяющие достигать результата быстрее или дешевле. Вся индустрия на них постепенно переключается, и если не идти в ногу с ней, то программный продукт становится легаси.</p><p>В другую группу можно отнести низкую культуру или плохую организацию процесса разработки.</p><p>Так незафиксированные требования к программному продукту могут и будут приводить к конфликтам между пользователями и разработчиками (и даже внутри группы разработки). По мере развития ПО необходимо вносить изменения и в документацию.</p><p>Отсутствие такой практики будет приводить к тем же проблемам. Пользователи будут сильно зависеть от экспертизы разработчиков. А если последние уволятся, то восстановления знаний из кода займет слишком много времени. В результате остается много вопросов «почему так сделали?» или «зачем это?».</p><p>Неверная архитектура или плохо спроектированный код провоцируют написание «костылей» (плохое решение, на момент написания которого мы знаем, что с ним будут или могут быть проблемы в будущем, однако которое решает проблему «здесь и сейчас»). Обсуждению архитектуры и проектирования можно посвятить отдельную статью или даже книгу, но если коротко, то нарушение общепринятых практик написания ПО, в частности, SOLID, будет приводить к проблемам, нежели позволит избегать их.</p><p>Выбраны неподходящие технологии (СУБД, фреймворки, библиотеки, или сторонние сервисы); не учтены особенности масштабирования.</p><p>Плохо написан код: невыразительные названия модулей, классов и переменных, слишком длинные функции (без явной необходимости), высокая цикломатическая сложность, дублирование кода.</p><p>Отсутствие тестов (лучше автоматизированных, но хотя бы ручных) приводит к внесению ошибок, которые могут быть не сразу обнаружены.</p><p>Сюда же отнесем плохо организованный проект: нет системы контроля версий; отсутствие задачника (системы фиксации запросов на изменения к системе); нет связи между коммитами и задачами (требованиями);</p><p>Отсутствие людей, которые имеют навык поддержки кода продукта, также делает код более “легаси”.</p><p>Основные причины возникновения легаси: устаревание технологий, плохие технические решения/практики; отсутствие требований/документации; нет людей знакомых с проектом;.</p><h2>Легаси с «положительной обратной связью»</h2><p>Важным свойством легаси является его разрастание. Если не прилагать усилий для минимизации, то с каждым изменением (и даже без них) степень легаси будет нарастать. Как во втором начале термодинамики энтропия замкнутой системы не может уменьшаться. Поэтому нам надо методично «вычерпывать воду из нашей протекающей лодки».</p><h2>Почему легаси неохотно заменяют</h2><p>Любой софт должен приносить IT-компаниям прямо или косвенно доход (или снижать издержки). Само по себе существование легаси бизнес мало волнует, пока вложенные усилия окупаются. В большинстве случаев, медленная доработка большой системы выгодней, чем переход на новую (где есть нужные изменения или где доработка ведется быстрее).</p><p>Минутка математики!<br />Можно представить, что легаси L(t) — это неотрицательная функция от времени. Пусть скорость разработки S(t) = F(L(t)), где F — некоторая функция, которая учитывает влияние легаси на скорость разработки при прочих постоянных факторах. Точный вид F неизвестен, но для нее верно: чем больше “легаси”, тем меньше скорость разработки, а при легаси равной 0 скорость максимальная F(0) = Fmax.<br />При этом польза компании V определяется как интеграл от затрачиваемых ресурсов:<br />V = ∫K·S(t)dt, где K — это сумма, которую бизнес готов вложить в разработку софта за интервал времени.<br />Если К представить в виде суммы Ks (деньги на пользу)  и Kl (деньги на борьбу с легаси), такие, что Ks + Kl = K, то, зная форму F, можно максимизировать V для компании.<br />И вот эта задача, которую в том числе решает СТО – определить форму F и максимизировать V ?</p><p>Однако поддержка системы может столкнуться с внезапной проблемой, например: в сторонней библиотеке обнаружена уязвимость. А библиотека не поддерживается и обновлений не будет. Вариантов решения может быть много: устранить проблему своими силами, заменить библиотеку, построить какое-то безопасное окружение, отказаться от функционала и пр.</p><p>Бывают случаи, когда легаси «починке/доработке» не подлежит, только замене. В такой ситуации перед бизнесом встанет выбор:</p><ul><li>отказаться от доработок (если это возможно);</li><li>отказаться от доработок в текущей системе и начать разработку новой;</li><li>перейти на готовое стороннее решение.</li></ul><p>Одна из главных задач СТО — минимизировать риски возникновения «внезапных проблем» и иметь план действий в случае их наступления.</p><p>Готовиться к переходу на новую систему нужно заранее, как правило, это занимает большое время. Нужно перенести данные и, главное, обучить людей работе в новой системе, что часто связано с изменением внутренних процессов. А такие изменения персонал не любит.</p><p>СТО заблаговременно должен отслеживать тенденции на рынке и продумывать план внедрения новых технологий в свои системы или планировать переход на другие решения, оценивая стоимость разных вариантов.</p><h2>Как бороться с Легаси</h2><ul><li>выделять время на мониторинг индустрии. Если какие-то технологии или ПО устаревают, надо планировать переход на новые технологии / обновление ПО;</li><li>организовать процесс разработки так, чтобы требования были четкие и зафиксированы в какой-то системе, чтобы изменения в коде были привязаны к этим требованиям;</li><li>покрывать систему тестами;</li><li>следить за актуальностью документации;</li><li>приостанавливать разработку и проводить рефакторинг кода в случаях, когда костылей становится слишком много (здесь важен баланс, так как постоянный рефакторинг в борьбе за «скорость разработки» отнимает время от разработки);</li><li>передавать владение кода от программиста к программисту: 1) больше людей знакомы – меньше рисков, что код остается бесхозным, 2) отработанная практика передачи владения стимулирует поддерживать код и документацию в надлежащем виде (в процессе передачи будут выявляться недостатки), придает уверенность, что новый человек сможет разобраться;</li><li>следить за чистотой кода (как минимум настроить линтеры, проводить ревью).</li></ul><h2>Польза от Легаси</h2><p>Итак, легаси присутствует в той или иной степени во всех компаниях, где есть разработка. Так какова же польза от него? Для бизнеса пользы никакой — при прочих равных, чем больше легаси, тем для компании хуже, уменьшение или поддержание легаси на заданном уровне стоит денег, поэтому каждая компания определяет этот уровень самостоятельно (конечно же, не по формулам выше, а по интуиции, но как-то определяет).</p><p>Однако польза от легаси может быть для сотрудников, с ней работающих. Особенно для новичков, приходящих в компанию. Во-первых, уровень зарплаты может быть выше, если в компании вы не первый, кто устраивается работать с «их легаси». Она может удерживать разработчиков, чтобы их код кто-то поддерживал. Во-вторых, уровень стресса может быть ниже, если компания привыкла к медленным внедрениям (оговорка, не везде и не всегда). Плюс сомнительный, но для кого-то это может показаться более комфортным местом. В-третьих, есть возможность изучить поглубже определенные технологии, используемые в этой компании — все равно легаси изучать, можно и документацию по технологиям почитать. Особый плюс для стажеров и джунов, которым нужно первое место работы — если в компании готовы их взять и помогать разбираться с кодом, даже решая простые задачи в сложной системе можно начать хорошую карьеру. Еще один плюс — можно посмотреть, как делать НЕ надо.</p><p>Если вы «застряли» в какой-то технологии, не отчаивайтесь, возможно, через несколько лет, вы станете редким высокооплачиваемым специалистом. ?</p><p>В США ряд старых банковских систем написаны на COBOL — устаревшем языке программирования. Поддерживать системы надо, а молодые специалисты не горят желанием изучать этот язык, поэтому специалистов не хватает. Ситуация настолько накалилась, что энтузиаст COBOL создал собственную компанию по оказанию экстренной поддержки банкам и обучению молодежи языку, что вслед за ним сделало и IBM.</p><p>От легаси никуда не денешься. Он всё равно будет появляться и с ним предстоит жить и бороться. Это стоит воспринимать, как вызов. Или как возможность, если вы ещё на старте карьеры.</p>]]></content:encoded>
    </item>
    <item>
      <title>Лучшие практики в код ревью. Главные ошибки и как их избежать</title>
      <link>https://tproger.ru/articles/luchwie-praktiki-v-kod-revyu--glavnye-owibki-i-kak-ih-izbezhat</link>
      <comments>https://tproger.ru/articles/luchwie-praktiki-v-kod-revyu--glavnye-owibki-i-kak-ih-izbezhat?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Viacheslav Aksenov]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/luchwie-praktiki-v-kod-revyu--glavnye-owibki-i-kak-ih-izbezhat</guid>
      <description><![CDATA[<p>Прошёлся по правилам пулл реквестов и код ревью. Рассказал, как сделать эти процессы максимально простыми и удобными для всех членов команды.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/luchwie-praktiki-v-kod-revyu--glavnye-owibki-i-kak-ih-izbezhat">Лучшие практики в код ревью. Главные ошибки и как их избежать</a>»</p>]]></description>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Wed, 07 Feb 2024 08:30:02 GMT</pubDate>
      <content:encoded><![CDATA[<p>Меня зовут Вячеслав Аксёнов, я бэкенд разработчик, с опытом более 5 лет. За свою карьеру я помогал строить сложные распределенные системы в крупнейших российских финтех компаниях, продуктовых европейских компаниях и сейчас выстраиваю процессы разработки в продуктовом стартапе в США.</p><p>За свой опыт я создавал и ревьювил сотни, если не тысячи пулл реквестов, видел множество разных подходов к код ревью и встречал достаточно много ошибок, которые делали код ревью неприятным и сложным процессом.</p><p>В этой статье я бы хотел подробно пройтись по сути пулл реквестов и код ревью и поделиться своими мыслями о том, как можно сделать эти процессы максимально простыми и удобными для всех членов команды.</p><h2>Что такое код ревью и пулл реквест</h2><p>Процесс командной разработки зачастую включает в себя работу несколькими людьми над одним репозитием. В таком процессе необходимо следить за тем, чтобы репозиторий был максимально надежным и имел только качественный код, который был бы хорошо читаемым и содержал бы минимальное количество багов. Для выполнения этих требований существует процесс код ревью – участник команды выставляет часть кода на проверку, которую проводят другие члены команды и либо оставляют комментарии с пометкой “требуется доработка”, либо ставят галочку “одобрено”. Код ревью важен для команд с разработчиками любого уровня, поскольку все члены команды хотя бы верхнеуровнево начинают быть в курсе что именно добавляет в репозиторий другой разработчик.</p><p>Чтобы было удобно проводить код ревью, во всех современных системах репозиториев есть механизм “Пулл реквеста” или “merge request” (в gitlab), что по сути одно и то же.</p><h2>Как интегрировано в жизнь разработчика</h2><p>Когда разработчик работает над задачей, он делает это в отдельной ветке и рано или поздно наступает момент, когда работа закончена и нужно влить эти изменения в главную ветку. Тут наступает время для процесса код ревью. Разработчик выставляет пулл реквест, добавляет описание и прикладывает ссылку на задачу. И дальше ждет реакции коллег. Коллеги выступают проверяющей стороной и должны проверить этот пулл реквест таким образом, чтобы не допустить попадания опасного или неправильно работающего кода в репозиторий. И сделать это таким образом, чтобы сохранить дружелюбные отношения внутри команды. На первый взгляд ничего сложного, но нюансов в “правильном” пулл реквесте и “правильно” проведенном код ревью очень много.</p><h2>Какие ошибки могут быть со стороны выставляющего пулл реквест</h2><h3>Создавать пулл реквест, содержащий несколько тикетов сразу</h3><p>Давайте представим, что у вас есть несколько маленьких тикетов – тут маленькую опечатку поправить, тут баг небольшой плюс тестик дописать, а здесь чуть-чуть класс подрефакторить. Очень велик соблазн собрать эти тикеты в один пулл реквест и провести единое код ревью. Если у вас в команде нет четкой договоренности по таким кейсам, то создавая один пулл реквест для всего повысит нагрузку на каждого участника команды, который прикоснется к такому код ревью.</p><p>Во-первых, чтобы проверить такой пулл реквест, проверяющим все равно придется погрузиться во все три тикета сразу. Плюс придется сделать это одновременно. Любое погружение в новый контекст – это трата ресурсов, а погружение сразу в три новых контекста – это еще большая растрата ресурсов, которые можно было бы сэкономить. Это повысит градус недовольства каждого проверяющего.</p><p>Во-вторых, не следует исключать возможность ошибки, которая всегда может быть пропущена. И в таком случае три фикса, запушенных и задеплоенных одновременно создают риск обнаружить эту ошибку уже в продакшене. И тогда чинить это придется либо на ходу каким-нибудь хот фиксом, либо откатывать весь пулл реквест со всеми тремя задачами, править его и передеплоивать. В любом случае – совершать такую работу, имея три тикета в контексте это кратно труднее, чем делать это для одного единственного тикета.</p><p>В третьих, три тикета в одном пулл реквесте значительно увеличивают вероятность появления комментариев и правок, что приведет к тому, что комментарии по одному тикету из трех сразу заблокируют вывод в продакшен остальных двух тикетов.</p><p><b>Как не допустить такой ошибки:</b> неукоснительно соблюдать правило – в одном пулл реквесте максимум один тикет.</p><h3>Слишком большой для одного тикета</h3><p>Бывают такие задачи, реализация которых занимает огромное количество кода. Можно много сказать про то, что является причиной, но как правило, раз вы дошли до код ревью, то уже поздно переформулировать задачу. И можно не долго думая выставить огромный пулл реквест, который будет содержать всю фичу сразу. Огромным в данном контексте называется все, больше 3к строк, что явно будет некомфортным для ревью.</p><p>В таком сценарии опять же есть риск того, что контекст, требуемый для проверки такого пулл реквеста, может быть слишком велик и отнимет много времени у каждого члена команды, причастного к этому ревью. Бонусом, правок в таком пулл реквесте может быть столько, что и сам разработчик будет не рад объему дополнительной работы.</p><p>А уж если пулл реквест оказался настолько запутанным, что никто из членов команды не может разобраться что именно и как там написано, то точно НЕ СТОИТ устраивать созвон на всю команду с демо своего пулл реквеста. Потому что только время свое и всех участников команды потратите, а к облегчению задачи едва ли приблизитесь</p><p><b>Как не допустить такой ошибки:</b></p><ul><li>Во-первых, оценить какие именно изменения были проведены в пулл реквесте. Где добавились новые модели, где добавились новые клиенты или интегарации, где рефакторинг зачесался.</li><li>Во-вторых, по возможности разделить огромный пулл реквест на пулл реквесты поменьше. Главное чтобы каждый был емким и завершенным. Добавили модели – отлично. Добавили внешнюю интеграцию + тесты – супер.</li></ul><p>Главная идея – создавать завершенные пулл реквесты, которые будет возможно проверять в один присест. Такие ревью будут проходить гораздо проще + будут приближать вас к завершению задачи.</p><h3>Добавлять закомментированный код</h3><p>Иногда кажется, что “да ладно, закомментирую эту функцию вместо того чтобы удалить”. Но это является ошибкой поскольку комментарии в коде катастрофически уменьшают читаемость кода. И каждый пулл реквест должен по возможности следовать правилу “сделай репозиторий лучше, чем он был до”. А добавление комментариев в пулл реквесте только множит хаос в репозитории. Если вы боитесь удалить что-то неиспользуемое, то держите в голове, что git – это машина времени для кода, в любой момент можно поднять историю и посмотреть как изменялся файл и что из него удалялось, а что добавлялось.</p><p>А если вы добавляете НОВЫЙ закомментированный код, то вероятно вы опасаетесь что он работает некорректно. Этого нельзя делать категорически. Закоментированный нестабильный код – это мусор.</p><p><b>t Как не допустить такой ошибки:</b> не добавляйте закомментированный код в пулл реквест. Никакой. Вообще. Единственным исключением может быть описание какого-то блока бизнес логики, если у вас такая практика закреплена в команде. Либо комментарий с указанием “todo + номер задачи с описанием что должно быть здесь доработано”.</p><p>Если же есть что-то неочевидное в коде, то указать на это комментарием в самом пулл реквесте.</p><h3>Запрашивать ревью пулл реквеста, который имеет сломанные тесты</h3><p>Как правило, все команды имеют настроенные CI скрипты (Continious Integration пайплайн), которые выполняют запуск тестов на вашей ветке чтобы убедится что код внутри правильно работает. Бывает такое, что во время реализации новой фичи вы либо написали тесты, которые не работают, либо каким-то образом сломали старые тесты. И этот самый CI пайплайн падает с ошибкой в одном из ваших тестов.</p><p>Кажется, что в такой ситуации можно написать примечания к пулл реквесту мол “не обращайте внимание, потом поправлю”. Но это ошибка – поскольку пулл реквест должен быть “завершенным” и ни в коем случае не сломать основную ветку проекта. А запрос ревью на пулл реквест, который заведомо “сломает” ветку, крайне снижает уровень доверия к такому пулл реквесту и скептицизм взлетает до совсем высокого уровня. Такой пулл реквест можно сравнить с отвлечением команды от работы. Поскольку текущее ревью явно будет не последним и тем самым обесценит время, затраченное на него.</p><p><b>Как не допустить такой ошибки:</b> чинить все неработающие тесты по обнаружению. И только после того, как все тесты были починены, а CI проверки пройдены, запрашивать ревью данного пулл реквеста. Уважайте время других членов команды.</p><h3>Запрашивать код ревью на код, не покрытый тестами</h3><p>Когда разрабатываешь новую бизнес фичу может показаться, что нет смысла тратить время и писать тесты. Ведь “там же все и так очевидно” или потому что “лень”. Но уверяю вас, добавление бизнес логики без тестов в репозиторию подобно бомбе замедленного действия. Будь вы хоть сколько опытным разработчикм, никогда не допускающим ошибок, наличие тестов позволит найти ошибку в будущем, если кто-то другой сломает ваш код. Пулл реквест, где код не покрывается тестами является незаврешенным и даже более опасным, чем иметь неработающие тесты. Поскольку в таком случае CI пайплайн будет “зеленым” и не подсветит ошибок.</p><p><b>Как не допустить такой ошибки: </b>всегда проверять, что ваш пулл реквест содержит тесты на тот код, который вы написали или исправили. Не допускать отсутствия тестов.</p><h3>Иметь в одном пулл реквесте код от нескольких разработчиков</h3><p>Бывают такие ситуации, когда вам потребовалась помощь от коллеги, который по доброте душевной решил запушить свои изменения в вашу ветку. В таком случае на этапе код ревью легко оказаться в ситуации, когда непонятно кто будет отвечать за тот самый код, запушенным другим разработчиком. И это увеличивает неопределенность в таком важном процессе как код ревью.</p><p><b>Как не допустить такой ошибки:</b> либо разделять свой пулл реквест и просить коллегу создать свой пулл реквест отдельно, либо перенимать его код и применять к своей ветке как есть, либо с исправлениями. Но не допускать смежного авторства в одном пулл реквесте.</p><h2>Какие ошибки могут быть со стороны принимающего пулл реквест</h2><h3>Придирки к мелочам и требовать переделать с таким же подходом, но “по-другому”</h3><p>Бывает такое, что открываешь пулл реквест на проверку и видишь, что решение отличается от того, которое представляется в голове. И в такой ситуации иногда можно воспользоваться правом “проверяющего” и поставить “требуются изменения” с указанием своего видения решения.</p><p>Однако это не всегда хорошая идея, поскольку как правило одна и та же задача может иметь множество решений. Код пишет разработчик, который уже в вашей команде и он – профессионал и, если вы ему доверяете, то следует допускать, что его видение может отличаться от вашего.</p><p>Если разработчик – новичок или не так опытен, то имеет смысл более внимательно относиться к его реквестам. Но даже в этой ситуации код ревью не должно быть процессом, где код причесывается под видение одного человека, даже если он “самый главный” в команде. Поскольку репозиторий – сущность, с которой работает команда, а не единственный человек.</p><h3>Быть токсичным</h3><p>День оказался неприятным, кто-то нагрубил, погода плохая, а тут еще пулл реквест этот, а в нем еще и ошибки какие-то. Бывает, что очень хочется написать “ну и чушь ты здесь понаделал”. И если взять во внимание, что вы – одна команда, которая работает над общим проектом, то становится очевидно, что токсичность даже одного члена команды может ухудшить атмосферу уважения во всем коллективе.</p><p>Токсичность – это непрофессионализм.</p><p>Проверка чьего-либо труда – это всегда более сильная позиция Когда вы выступаете в оценивающей роли – следует это учитывать и быть максимально вежливым к человеку, выставившему пулл реквест. Даже если у него есть ошибки, даже если логика написана неоптимально или переменные названы нечитаемо.</p><p>Признаком профессионала будет оставление на каждую ошибку емкого комментария с описанием почему так делать не надо и как делать надо. Можно оставить ссылку на статью по теме с комментарием. Любое требование изменения должно быть аргументированным. И быть написано максимально спокойным или дружеским тоном.</p><h3>Пытаться найти максимальное количество ошибок</h3><p>Вы можете быть гораздо более опытным разработчиком, чем тот, кто выставил пулл реквест. Но это никаким образом не должно влиять на подход, с которым вы проводите ревью. Ревью должно проводиться как будто его выставил равный вам профессионал. Все могут допускать ошибки и это нормально.</p><p>Однако код ревью не является соревнованием по поиску недоработок. Любой код не идеален по своей сути. И требование код ревью – не допустить опасного или вредного кода в репозиторий. Но не искать где можно придраться к код стайлу или неверному отступу.</p><p>Следует указывать только на критические функциональные ошибки, либо нарушение договоренностей внутри команды.</p><h3>Откладывать пулл реквест в долгий ящик</h3><p>Вот это денек выдался – столько задач, столько созвонов, а еще эти код ревью поназапрашивали. Ничего не случится если завтра проверю, ну или послезавтра.</p><p>Если следовать такой идее, то легко попасть в ситуацию, когда код ревью станет столько, что на их проверку уйдет сразу несколько дней. И подводный камень такой ситуации, что все пулл реквесты могут начать конфликтовать друг с другом при мердже. Чтобы разобраться со всеми этими конфликтами и замерджить все в главную ветку может потребоваться едва ли не столько же времени чтобы провести все код ревью. Поэтому код ревью следует проводить регулярно, не позволяя пулл реквестам накапливаться.</p><p>Как правило внутри команды заводится четкая практика – например, код ревью проводится строго с утра первым делом. Такая практика делает срок получения комментариев по пулл реквесту предсказуемым.</p><h2>Выводы</h2><p>Хороший пулл реквест – это какой?</p><ol><li>Имеет понятное название + содержит ссылку или номер задачи.</li><li>Имеет тесты на все изменения.</li><li>Имеет пройденные проверки CI пайплайна.</li><li>Достаточно небольшой чтобы его можно было проверить в один заход.</li><li>Имеет только одного автора.</li></ol><p>Хорошее ревью – это какое?</p><ol><li>Своевременное.</li><li>Без токсичности, вежливое и хорошо читаемое.</li><li>Без излишней придирчивости к каждой строчке. Подсвечивает явные ошибки или потенциальные проблемы.</li><li>Не содержит требований “вам кажется, что можно написать по другому”. Если есть предложения – выдвигать их, но не требовать чтобы разработчик их выполнил.</li></ol><h2>Короткое дополнение для джунов</h2><ol><li>Если вас строго проверяют – это может быть как синдром вахтера, так и искреннее желание помочь вам развиться. Внимательно относитесь ко всем комментариям.</li><li>Пулл реквесты и код ревью это не страшно. У всех бывают тяжелые код ревью и легкие. Это необходимый процесс чтобы код в репозитории был чистым и работал.</li></ol>]]></content:encoded>
    </item>
    <item>
      <title>Гайд по чистому коду: учимся писать тесты</title>
      <link>https://tproger.ru/articles/gajd-po-chistomu-kodu-uchimsya-pisat-testy</link>
      <comments>https://tproger.ru/articles/gajd-po-chistomu-kodu-uchimsya-pisat-testy?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/gajd-po-chistomu-kodu-uchimsya-pisat-testy</guid>
      <description><![CDATA[<p>Покрываем интеграционным тестом небольшой сервис: объясняем, как сделать это локально и с помощью тест-контейнеров.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/gajd-po-chistomu-kodu-uchimsya-pisat-testy">Гайд по чистому коду: учимся писать тесты</a>»</p>]]></description>
      <category><![CDATA[Тестирование]]></category>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 05 Dec 2023 10:58:54 GMT</pubDate>
      <content:encoded><![CDATA[<p>В этой части разберемся, что делать, чтобы программа работала как, как задумано. Посмотрим, что желательно подготовить до того, как браться за тестирование кода, настроим тест-контейнеры и wiremock. И, наконец, напишем интеграционный тест на бизнес-процесс.</p><h2>Сперва опишем в README раздел «Назначение сервиса»</h2><p>Чтобы все выглядело понятно, сперва «покопаемся» в теме: проанализируем документацию, если таковая есть, поговорим с экспертами. Они могут простыми словами объяснить, что должен делать сервис.</p><p>Далее кратко, в функциональном стиле, распишем, какую задачу он решает и что происходит по процессу от поступления запроса в сервис до его завершения. Будет круто, если мы визуализируем процесс. Времени потратим немного, а коллеги скажут спасибо.</p><p>Такой подход учит начинающих разработчиков делать все правильно с самого начала и последовательно оставлять артефакты, которые помогут «братьям по оружию» в будущем — новички учатся на наших с вами репозиториях.</p><p>Получим <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/tree/feature/01-readme">первый бранч и MP</a>. Для удобства важное вынесем в текст:</p><h2>Назначение сервиса</h2><p>Сервис является синхронным перекладчиком запросов систем-потребителей в системы-поставщики. Запросы поступают из систем-потребителей по очередям и обрабатываются сервисом-поставщиком согласно обобщенному бизнес-процессу:</p><ul><li>запросы из очереди направляются синхронно по REST в соответствующую систему;</li><li>полученный ответ упаковывается в ответный конверт и отправляется в очередь ответа.</li></ul><p>Подробнее в описании каждого процесса.</p><p>Обработка запроса из Платежной Системы (PipelineServicePS.kt)</p><ul><li>получает конверт с запросом;</li><li>валидирует входящий запрос;</li><li>отправляет квитанцию о результате валидации и принятии запроса;</li><li>отправляет запрос на получение истории операций в Систему А (REST);</li><li>отправляет полученный ответ в очередь;</li><li>отправляет квитанцию об успехе либо ошибки обработки запроса.</li></ul><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/c6df114a-f850-4740-a938-852ecf558c30.png" alt="" /></figure><h2>Теперь перейдем к тестам</h2><p>Напишем интеграционный тест на проверку работоспособности системы. Он дорогой, но покроет максимум кода, позволит убедиться, что система работает, и придаст уверенности при рефакторинге.</p><p>В процессе будем использовать инструменты:</p><ul><li>Testcontainers — поможет поднять IBM MQ в контейнерах на время исполнения тестов;</li><li>Wiremock — прекрасный инструмент, чтобы поднять заглушку REST-ресурса на время исполнения тестов.</li></ul><p>Полезные ссылки по теме</p><ul><li><a href="https://youtu.be/PEVVvZOt7bY?si=7n3YSuTo6k38aiQU">TestContainers — интеграционное тестирование с Docker</a></li></ul><ul><li><a href="https://java.testcontainers.org/">https://java.testcontainers.org/</a></li></ul><ul><li><a href="https://java.testcontainers.org/features/files/">https://java.testcontainers.org/features/files/</a></li></ul><h3>Конфигурация тестового профиля</h3><p>Зачищаем конфигурацию перед тестированием: <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-readme/src/test/resources/application-test.yml">/src/test/resources/application-test.yml</a></p><p>Если вы посмотрите в бранче с README <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-readme/src/test/resources/application-test.yml">содержание файла конфигурации application-test.yml</a>, то увидите много лишнего: конфиг содержит 49 строк, при этом в основном дублирует настройки основного профиля. Это в какой-то момент приведёт к такой рассинхронизации с основным профилем, что тесты станут давать ложноположительные или ложноотрицательные срабатывания.</p><p>Чтобы этого избежать, лучше вынести общие конфигурации в main/resources/application.yml. А в тестовом профиле оставить записи, которые переопределяют основные. Как правило, application-test.yml содержит переменные, зависимые от окружения.</p><p>В результате получим 14 строк конфигурации против 49. Конфиг будет содержать только то, что нужно для локального тестирования.</p><p>Теперь есть смысл <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/resources/application-test.yml">посмотреть на содержание</a>:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/4a8115a7-1453-4cd9-b05d-80d2b575dc59.png" alt="" /></figure><p>Я описал <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/tree/feature/01-integration-tests/src/test/resources/docker">разные способы игр с докером IBM MQ</a> в исходниках. Например, чтобы поднять докер с очередями, нужно зайти в папку docker cd src/test/resources/docker.</p><h3>Для локального тестирования посредствам монтирования раздела поднимем MQ</h3><p>docker run \</p><p>–env LICENSE=accept \</p><p>–env MQ_QMGR_NAME=QM1 \</p><p>–publish 1414:1414 \</p><p>–publish 9443:9443 \</p><p>–publish 9157:9157 \</p><p>–mount type=bind,source=./src/test/resources/docker/20-config.mqsc,target=/etc/mqm/20-config.mqsc \</p><p>И получим брокер с очередями, <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/resources/docker/20-config.mqsc">которые забираются из файла 20-config.mqsc</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/f9b93345-5a8a-432f-879e-f9e5e41183cf.png" alt="" /></figure><p>Далее можно написать <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/kotlin/team/codemonsters/refactoringexample/service/MqSimpleTest.kt">простой тест конфигурации подключения к брокеру</a>. Отправим в тестовую очередь MY.TEST сообщение и прочитаем его из очереди. Это позволит убедиться в работоспособности тестовой инфраструктуры.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/8b758e0f-4541-4e88-8e9f-b06757cc75f2.png" alt="" /></figure><p>Можно убрать наследование тестового класса от :AbstractIbmMqIntegrationTest() на 16 строке. Затем — запустить из папки /src/test/resources/docker/ команду в терминале, которую мы описали выше, и запустить тест. Так, в логах появятся отклики на тест MQ.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/4a3145fb-d1ff-403c-b6ef-8a9b0d288995.png" alt="" /></figure><p>При работе с легаси или кодом без тестов и без документации такой метод может пригодится, чтобы понять, что всё точно сделано правильно.</p><p>Рекомендуем тестировать новые инструменты, библиотеки — это поможет сократить расходы и на раннем этапе убедиться, что разработка пошла в правильном направлении. Чаще всего разработчики этого не делают — а зря.</p><h3>Настраиваем тест-контейнеры</h3><p>Нам понадобится родная документация фрэймворка:</p><ul><li><a href="https://java.testcontainers.org/quickstart/junit_5_quickstart/">https://java.testcontainers.org/quickstart/junit_5_quickstart/</a></li><li><a href="https://testcontainers.com/guides/testcontainers-container-lifecycle/">https://testcontainers.com/guides/testcontainers-container-lifecycle/</a></li><li><a href="https://java.testcontainers.org/examples/">https://java.testcontainers.org/examples/</a></li></ul><p>Добавляем импорт либ для тестирования на тест-контейнерах в build.gradle, строка 35.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/15f78037-de15-4340-b57e-c2d6ee92053f.png" alt="" /></figure><p>Исследуем документацию про <a href="https://testcontainers.com/guides/testcontainers-container-lifecycle/">Управление жизненным циклом контейнеров тест-контейнеров с использованием JUnit 5</a>. Пробуем запустить паттерн Singleton Containers для запуска тестов на одном контейнере.</p><h4>Создадим класс AbstractIbmMqIntegrationTest</h4><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/371efc29-34c6-4d86-bca6-bf6a73af3aa0.png" alt="" /></figure><p>В нем опишем сам контейнер, строка 16.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/c2fba6fb-b114-48c9-a70b-9c58bd85bc97.png" alt="" /></figure><p>Обратите внимание на строку 21 — так можно подмонтировать файл и сконфигурировать стандартный контейнер. Эта полезная фича описана в доке <a href="https://java.testcontainers.org/features/files/">https://java.testcontainers.org/features/files/</a></p><h4>Установим переменную окружения для подключения к MQ</h4><p>Метод setIbmMqProperties, строка 29, заполняем ibm.mq.connName.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/e48fae71-4e27-42ef-b41a-0a195d64ebe4.png" alt="" /></figure><h4>Запустим контейнер при создании класса</h4><p>Функция на строке 37 запускает контейнер перед запуском тестов.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/392024ba-5310-4318-acff-f894c5a62e59.png" alt="" /></figure><h4>Напишем тест на инфрастурктуру</h4><p>Убедимся, что этому инструменту можно доверять, затратив минимум внимания и энергии. Ключевыми показателями успеха будут:</p><ul><li>простота документации;</li><li>простота реализации примера по документации;</li><li>работоспособность экспериментального кода по документации.</li></ul><p>Получим класс, который уже видели:</p><p><b>MqSimpleTest</b></p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/6bd68e6f-306a-4bfc-9c04-52beaaec12ec.png" alt="" /></figure><p>Запустим тест и получим зачаток «грини-котлин приложения» (приложения на котлин с зелеными тестами). Тест работает, доки качественные, и мы не плясали с бубном.</p><p>Проверено: Фрэймворк testcontainers помогает писать интеграционные тесты.</p><h3>Настраиваем WireMock</h3><p>Чтобы локально в тестовом окружении поднять и сконфигурировать заглушку для REST-ресурса можно взять два классных инструмента:</p><ul><li>WireMock: <a href="https://wiremock.org/docs/solutions/spring-boot/">https://wiremock.org/</a></li><li>Mock Server: <a href="https://www.mock-server.com/">https://www.mock-server.com/</a></li></ul><p>В документации Wiremock найдем рекомендацию того, как с ним работать в Spring:</p><ul><li><a href="https://wiremock.org/docs/solutions/spring-boot/">https://wiremock.org/docs/solutions/spring-boot/</a></li><li><a href="https://github.com/maciejwalkowiak/wiremock-spring-boot">https://github.com/maciejwalkowiak/wiremock-spring-boot</a></li></ul><p>Добавим зависимость в build.gradle</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/838708b8-5ce0-4bd5-9113-81d66940578c.png" alt="" /></figure><p>Добавим <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/resources/wiremock/rest-api-gateway/mappings/wallet-balance.json">конфигурацию ответа сервиса rest-api-gateway</a>. Проверим, что мок-сервер поднимается и подгружает конфигурации ответов из файлов.</p><h4>Напишем тест на инструмент в классе WireMockWithStubsTest</h4><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/63ffc462-2d98-4f33-aa54-c70facc88ded.png" alt="" /></figure><p>Строкой 15 описываем инстанс мок-сервера с именем rest-api-gateway. Он определяет url конфигурации рест ресурса rest.configs.api-gateway.url:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/281acd13-3ba4-4362-abb4-61105eaa167e.png" alt="" /></figure><p>Этот url загружается в контекст приложения из <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/main/resources/application.yml#L81">/src/main/resources/application.yml, строка 81</a>.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/78ab8901-5081-4dbe-8331-141c7a2901b5.png" alt="" /></figure><p>Он используется в классе <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/main/kotlin/team/codemonsters/refactoringexample/configuration/RestConfiguration.kt">RestConfiguration</a>, а далее — страшным классом <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/main/kotlin/team/codemonsters/refactoringexample/client/RESTClient.kt">RESTClient</a>, который создатель наделил монструзоным методом на 80 строк <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/main/kotlin/team/codemonsters/refactoringexample/client/RESTClient.kt#L26C9-L26C20">sendRequest</a>.</p><p>Таким образом, мы понимаем, что приложение на шаге «Отправить запрос история операций по кошельку банка (в систему А по Ресту)» будет обращаться к рест-ресурсу по конфигурации rest.configs.api-gateway.</p><h4>Настроим и протестируем заглушку</h4><p>Строка 21 конфигурирует WebTestClient на url рест-ресурса, который мокает wiremock:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/8381bee9-88a2-48b3-8a71-1a77ca5b9d40.png" alt="" /></figure><p>Строка 26 тестирует конфигурацю мок-сервера, которую он подтягивает из <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/resources/wiremock/rest-api-gateway/mappings/wallet-balance.json">src/test/resources/wiremock/rest-api-gateway/mappings/wallet-balance.json</a></p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/80a8ab6d-c6f8-4d5b-90a2-cc9d9d2849fd.png" alt="" /></figure><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/8e0a6891-c6a8-4ad5-be4c-776c7a45733f.png" alt="" /></figure><p>Конфигурация содержит описание запроса:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/91e8f90b-c997-40eb-b1cf-2239407a0eb2.png" alt="" /></figure><p>И ответа:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/06675604-8394-46b9-8294-c81c5ccbb3cd.png" alt="" /></figure><p>Подробнее про файлы конфигурации запросов, ответов, матчинга ответов в зависимости от параметров в теле запроса, заголовках написано <a href="https://wiremock.org/docs/request-matching/">в документации Wiremock</a>.</p><p>Тест «подмигнул» зеленым. То есть оба теста на инфру успешны, и мы можем:</p><ul><li>отправить сообщение в очередь;</li><li>прочитать сообщение из очереди;</li><li>отправить http-запрос на REST-ресурс и получать ответ на него.</li></ul><h3>Напишем интеграционный тест, который отработает недоступность REST-ресурса</h3><h4>PipelineServicePsFailTest</h4><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/093f76ae-f9c4-4b81-bd84-2a40c6494cdd.png" alt="" /></figure><p>В случае сбоя на шаге отправки REST-запроса квитанция (строка 69) должна содержать сообщение об ошибке. Код, который описывает пайплайн бизнес-процесса, работает некорректно — возвращает Success.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/1b799164-0f1e-4c62-9ddb-33bc30afb620.png" alt="" /></figure><p>Исправим функцию assertReceiptError так, чтобы она была достоверной и утверждала правильное поведение системы. Тест теперь красный, именно это нам и нужно.</p><p><a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/kotlin/team/codemonsters/refactoringexample/service/PipelineServicePsFailTest.kt">Исходник</a>. Строка 112 — ReceiptStatus. ERROR — ожидаемый статус в квитанции. Тест защищает код от бага красным оком контроля качества интеграционного-теста.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/3ea59399-2d4b-4799-a83c-611fca80f894.png" alt="" /></figure><h3>Опишем явно слушатель и уберем лишние классы</h3><p>Слушатели динамично создаются в MqServiceConfiguration:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/7586a1a7-d3b9-4cca-a4ab-05178e07a2ef.png" alt="" /></figure><p>Вместо кода на 64 строки, который нужно осознать, простить и принять, мы можем просто описать слушатель на очередь запросов по бизнес-процессу четырьмя строчками кода в классе <a href="https://git.codemonsters.team/guides/mq-rest-sync-adapter/-/blob/feature/01-INTEGRATION-TESTS/src/main/kotlin/team/codemonsters/refactoringexample/service/PipelineServicePS.kt?ref_type=heads#L47">PipelineServicePS</a>:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/9a7c27db-cc91-481d-b609-b4ba872b9c37.png" alt="" /></figure><p>Даже если у нас 5–7 процессов лучше описать слушатель явно — так, появляется очевидная точка входа и код проще понять.</p><h3>Разберем интеграционный тест успешного выполнения бизнес-процесса</h3><p>Обобщая, поведение <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/blob/feature/01-integration-tests/src/test/kotlin/team/codemonsters/refactoringexample/service/PipelineServicePsSuccessTest.kt">системы</a> (SUT — System Under Test) должно быть таким: на вход в очередь запросов мы отправляем конверт с запросом и проверяем, что в ответных очередях есть сообщения.</p><p>Очередь для квитанций содержит два сообщения:</p><ul><li>сообщение об успешном принятии запроса;</li><li>сообщение об успехе обработки запроса.</li></ul><p>Очередь для ответа содержит результат взаимодействия с REST-ресурсом.</p><p>Рассмотрим тест, который проверяет, что система работает правильно. И после — конфигурацию тестовой среды, которая обеспечит его успешное выполнение.</p><h4>successful wallet balance request</h4><p>Название понятное и сообщает нам, что тест проверяет успешный запрос баланса кошелька:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/0cc20ef4-4a1c-4bfe-9409-b9f0e1b097ee.png" alt="" /></figure><p>Простой понятный тест, который является документацией, написан по <a href="https://freecontent.manning.com/making-better-unit-tests-part-1-the-aaa-pattern/">паттерну 3A</a>:</p><ul><li>Arrange: подготавливаем сообщение, конверт с запросом createRequestEnvelope (строка 66), чтобы отправить его в очередь запросов IN.QUEUE.PS;</li><li>Act: отправляем сообщение в очередь запросов publishToQueue, воздействуем на SUT (строка 71);</li><li>Assert: проверяем утверждения с фактическими данными, которые выдает SUT, вызвав метод с понятным названием assertResponsesInQueues (строка 73).</li></ul><p>Пробежимся по ключевым методам теста: createRequestEnvelope, publishToQueue, assertResponsesInQueues, убедимся, что наш тест является документацией и его легко понять.</p><h4>createRequestEnvelope</h4><p>Этот метод просто и понятно описывает процесс создания корректного конверта с запросом.</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/bf19c583-aaa7-4324-9847-f1fbab83c50a.png" alt="" /></figure><h4>publishToQueue</h4><p>Метод прост: используем jmsPublisher: MqPublisher для отправки сообщения в очередь</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/fc0657e4-9acb-4384-9a8b-fc216c643110.png" alt="" /></figure><h4>assertResponsesInQueues</h4><p>Проверяем, что бизнес-процесс отработал корректно:</p><ul><li>в очереди две квитанции со статусами ReceiptStatus.SUCCESS (строка 77) и ReceiptStatus.ACCEPTED (строка 78);</li><li>в очереди ответа ожидаемое сообщение ответа сервиса (строка 79).</li></ul><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/a7084eaa-be70-4d00-8b03-028c3436327b.png" alt="" /></figure><h4>assertReceiptSuccess</h4><p>Тестируем факт чтения квитанции об успехе операции:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/41b92f84-4455-4428-aa7a-7852e9b39a86.png" alt="" /></figure><h4>assertReceiptAccepted</h4><p>Тестируем факт чтения квитанции о том, что запрос получен и взят в работу:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/82cc3569-c525-48ca-ac63-36e85bfb69f5.png" alt="" /></figure><h4>assertResponse</h4><p>Проверяем ответ в очереди ответа, должно быть все понятно из содержания метода:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/bfe767aa-2181-4a4b-be52-9fa3554dbfd7.png" alt="" /></figure><h4>assertThatResponse</h4><p>Добрались до содержания, в котором видно, какой ответ мы ожидаем получить в очереди ответов:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/c67d6f73-48a7-47eb-a68d-0fa6f7e76d72.png" alt="" /></figure><h3>Рекомендуем использовать в тестах Soft Assertions</h3><p>Обратите внимание: во всех блоках с проверкой утверждений, например, как в assertThatResponse, мы рекомендуем использовать Soft Assertions. Это удобная фича, которая позволяет увидеть все фэйлы теста одним сообщением.</p><p>Такая есть в <a href="https://assertj.github.io/doc/">fluent assertions java-либе assertJ</a>, которую мы предпочитаем для работы с утверждениями, и рекомендуем разработчиками. Блок теста на проверку ответа с Soft Assertion выглядит так:</p><h4>assertThatResponse</h4><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/e2d5ee0a-e1b1-4c23-bf33-080a4063a7c2.png" alt="" /></figure><p>Все ошибки краснеют в терминале сразу, показывая, где утверждения в тестах разошлись с фактами:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/4c7624c5-38b5-4fa0-a037-cf747559cd03.png" alt="" /></figure><p>Это удобнее, чем править утверждение за утверждением и перезапускать тест множество раз.</p><h2>Конфигурация успешного теста</h2><h3>Контейнер Ibm MQ</h3><p>Данная конфигурация отличается от конфигурации MqSimpleTest, которую я использовал для тестирования фрэймворка testContainers. Простой инфраструктурный тест опирался на шаблон singleton container, а нам понадобится запустить тесты изолированно друг от друга.</p><p>Чтобы протестировать ошибочный и успешный пути исполнения тестов, запустим каждый тест со своим контейнером. Это дорого по ресурсам (cpu, mem), но мы гарантированно получаем <a href="https://testcontainers.com/guides/testcontainers-container-lifecycle/">изолированный запуск тестов</a>.</p><p>Я для простоты выбрал подход использования методов обратного вызова жизненного цикла JUnit 5 (<a href="https://testcontainers.com/guides/testcontainers-container-lifecycle/#_using_junit_5_lifecycle_callback_methods">Using JUnit 5 lifecycle callback methods</a>):</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/9625b7f0-66c6-4a38-ab75-e9da897164fc.png" alt="" /></figure><p>На строке 38 создадим контейнер, описанный в классе IbmMqContainer:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/fedb3a39-2ba5-4d44-baa8-ceeabca89c76.png" alt="" /></figure><p>В строках 40–46 установим переменную среды ibm.mq.connName для подключения к IBM MQ:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/e58210ed-6855-4de4-9f71-fbbc2c74fc5b.png" alt="" /></figure><p>Строки 48–58 отвечают за запуск и остановку контейнеров:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/74ab2e24-8a5e-4485-98ff-78d88c4a747a.png" alt="" /></figure><p>JUnit 5 сначала вызовет метод startContainers(), а затем выполнит все тесты, помеченные аннотацией @Test, Как только все тесты будут выполнены, JUnit 5 вызовет метод обратного вызова @AfterAll (строка 55).</p><h3>Конфигурация WireMock</h3><p>В предыдущем разделе мы подробно рассмотрели и протестировали необходимую нам конфигурацию WireMock. В тесте прописываем ее в строке 29:</p><figure><img src="https://media.tproger.ru/user-uploads/23823/2023-12-04/a71c71b7-3275-4165-bc3d-1120e317d1ba.png" alt="" /></figure><p>Этого достаточно, чтобы протестировать успешно бизнес-процесс.</p><h2>Несколько правил в заключение</h2><ul><li>Чтобы защитить себя перед рефакторингом, напишите интеграционный тест на успешное выполнение бизнес-процесса и на один фэйл.</li><li>Остальные крайние точки протестируйте юнит-тестами в отрефаченном коде.</li><li>Пишите тесты на новые инструменты, инфраструктуру — это гарантирует вам продвижение к последующим улучшениям. Проверено — работает.</li></ul><h3>И пара рекомендаций</h3><ul><li>Более подробно про лучшие практики 3A рекомендуем прочитать в статье Владимира Хорикова <a href="https://freecontent.manning.com/making-better-unit-tests-part-1-the-aaa-pattern/">Making Better Unit Tests: part 1, the AAA pattern</a>.</li><li>Также рекомендуем каждому разработчику купить и прочитать книгу Владимира:<a href="https://www.piter.com/product/printsipy-yunit-testirovaniya">«Принципы Юнит-Тестирования»</a>, которая вобрала в себя прекрасные эссенции о тестировании.</li></ul><ul><li>Для таких же неугомонных любителей тестирования, как и мы, рекомендуем прекрасный Доклад про DDD от Ion Cooper’а TDD, Where Did It All Go Wrong (Ian Cooper).</li></ul>]]></content:encoded>
    </item>
    <item>
      <title>Как не стоит писать код: разбираем ошибки</title>
      <link>https://tproger.ru/articles/kak-ne-stoit-pisat-kod-razbiraem-owibki</link>
      <comments>https://tproger.ru/articles/kak-ne-stoit-pisat-kod-razbiraem-owibki?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-ne-stoit-pisat-kod-razbiraem-owibki</guid>
      <description><![CDATA[<p>Вторая часть цикла статей про чистый код. В ней покажем пример некачественного кода и разберём основные ошибки.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-ne-stoit-pisat-kod-razbiraem-owibki">Как не стоит писать код: разбираем ошибки</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 29 Aug 2023 11:16:26 GMT</pubDate>
      <content:encoded><![CDATA[<p>В <a href="https://tproger.ru/articles/kak-napisat-chistyj-kod-i-sdelat-zhizn-proshhe/">прошлой части</a> мы рассказали, что такое чистый код и какие принципы нужно соблюдать, чтобы его написать. В новой — поговорим про плохой код: перечислим проблемы и покажем, как их решать.</p><p>Эта статья — «прожарка». Она не отвечает на все вопросы и не содержит иллюстрации ко всей критике. В будущем мы шаг за шагом более атомарно будем всё разгребать.</p><h2>Сперва «высечем» тезисы: как нельзя писать</h2><p>Всё, рассказанное ниже, база, которую мы взяли из своего опыта, и теперь хотим поделиться.</p><ul><li>НЕ пишите, если не нравится — круто, если код вас увлекает, и время в компании IDE летит. Но если нет — выберите что-то кроме программирования. В IT много интересной работы.</li></ul><ul><li>НЕ пишите слепо по ТЗ аналитика — лучше прописывать требования совместно (или хотя бы разобраться, что конкретно хочет аналитик). Потому что код — ответственность разработчика, и разгребать проблемы придется именно ему.</li></ul><ul><li>НЕ пишите, пока кратко и четко не описали проблему — её нужно зафиксировать в README репозитория на понятном пользователю языке. В идеале — описать поведение системы так, чтобы его можно было легко переложить в тесты. Это поможет смотреть на систему с перспективы её поведения.</li></ul><ul><li>НЕ пишите без предварительного проектирования — можно заранее визуализировать архитектуру приложения. Спросите себя, из каких компонентов будет состоять приложение, почему именно из них, какие роли будут исполнять классы на каждом уровне.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/96cefea9-66f4-4cb3-864b-02524d0b8109-autoconverted.jpeg" alt="" /></figure><h2>А теперь — сам «плохой код»</h2><p>Представьте, вы — разработчик. Только пришли в команду, познакомились с классными коллегами. И увидели… сервис. <a href="https://github.com/codemonstersteam/mq-rest-sync-adapter/tree/01-dev-origin">Такой, как этот</a>. Читать код невозможно, тестов почти нет, каждый класс «кусается» (ещё и на ChatGPT не свалишь).</p><p>Говорить про тесты здесь не будем — это большая тема, которой лучше посвятить отдельный текст.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/c87b071b-8de9-44fa-bd26-ff74a9fce829.png" alt="" /></figure><p>Если вы читали Роберта Мартина или, как минимум, мою предыдущую статью — появится мысль: «Нужно срочно покинуть эту команду». Но лучше выдохнуть и проанализировать код — его можно превратить в гармоничное приложение, которое легко прочитать, понять и, если нужно, доработать.</p><h3>Сканируем методы, классы и структуру пакетов.</h3><p>Мы постарались сделать «прожарку» максимально понятной. Но всё равно может быть тяжеловато.</p><h4>Пакет client.RESTClient</h4><p>Довольно простой REST-клиент на базе WebClient. По сигнатуре функция sendRequest соответствует нашему гайду. Но 82 строки для подобного класса — это много. Так что стоит упростить: разбить код на блоки и вытащить каждый в отдельную функцию (метод класса в ООП).</p><p>Также RESTClient важно покрыть тестами.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/99a0e0c5-6690-4f6f-85b8-a7429ac7f11a.png" alt="" /></figure><h4>Пакет configuration</h4><p>MqConfiguration — контейнер конфигурации, который содержит класс MqServiceCfg. Захардкоженные значения полей timeout, messagePipeline и type смущают. Лучше вынести значения в конфиг-файл.</p><p>Класс WebClientConfiguration стоит проверить тестами.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/b6356595-e3e9-4df5-a097-cfdbaa6ea856.png" alt="" /></figure><h4>Пакет exceptions</h4><p>ProcessException — класс, который нигде не используется. Просто удалить.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/8f92d651-ce87-46a7-8463-12c20065ad3a.png" alt="" /></figure><h4>Пакет mq</h4><p>Опустим классы-контейнеры без инкапсулированной логики работы с данными. И остановлюсь на проблемах в других классах.</p><p>GetWalletBalanceRequest</p><p>Смарт-конструктор emerge может выкинуть исключение на этапе десериализации из файла.</p><p>Нужно это предусмотреть и обработать. Например. обернуть Result.runCatching{ }. Тогда emerge будет возвращать <a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-result/">Result</a>, то есть однозначно говорить о том, что функция (метод) может зафэйлиться при исполнении и вернуть <a href="https://fsharpforfunandprofit.com/posts/recipe-part2/">Two Track Type</a> — или результат выполнения в поле success или failure.</p><p>Если вы отдельно обрабатываете исключения, можно обернуть их в удобный тип.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/0fcc62d0-adbe-4198-ab13-1d41d44a3248.png" alt="" /></figure><p>Валидацию формата полей класса dateFrom, dateTo лучше вынести в функцию emerge и если на вход влетели битые данные — вернуть понятную ошибку.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/f593f860-ec2c-4f92-8dab-6151be9c4dec.png" alt="" /></figure><p>HttpRequestEnvelope</p><p>Класс — DTO, принадлежит слою представления, перегружен логикой, которой в DTO вообще быть не должно по определению. Её нужно вынести в соответствующие классы уровня бизнес-логики.</p><p>Поле класса body (31 строка) лучше сделать пустой строкой по-умолчанию — так приближаемся к Null Safety universe.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/3fb7164a-a4c1-4ae2-919c-3677558e76b8.png" alt="" /></figure><p>Функция emerge</p><p>Поля просто перекладываются из входных параметров функции в поля класса. Умный конструктор говорит «тут возможна ошибка создания валидного объекта, поэтому я верну Result», который нам не нужен. Поэтому лучше заменить его на простой of метод.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/b3fa83bb-2df6-44c5-a5e5-9989a3d80cee.png" alt="" /></figure><p>Функция putPsRequestInBody</p><p>В первую очередь — что за заклинание вместо названия? Переименовать.</p><p>Нэйминг — это важно. Но мы на данном этапе не знаем, какое имя выбрать, нужно будет погрузиться в аналитику.</p><p>Во-вторых, в 71 строке мы генерируем json-строку из класса, который собирается из тела body-запроса. И если GetWalletBalanceRequest зафэйлится и не сможет собраться из строки запроса, которая должна содержать JSON, мы получим необработанный Exception. Это плохо, так в коде преднамеренно заложена ошибка и не заложена обработка этой ошибки.￼</p><p>Лучше собрать класс в одном месте с HttpRequestEnvelope и GetWalletBalanceRequest.</p><p>В-третьих, в 63 строке вместо fold стоит использовать Result.map — он для этого и создан. Или вовсе — передавать на вход в данный метод не Result, а HttpRequestEnvelope.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/b64b7630-be03-4e25-8026-5bdf639445f7.png" alt="" /></figure><p>Проектируйте систему так, чтобы она работала с валидными объектами — <a href="https://fsharpforfunandprofit.com/posts/designing-with-types-making-illegal-states-unrepresentable/">это возможно</a> и помогает смоделировать процесс более надёжно. Так, на слое, где можно вызвать «Пут Пс Реквест Ин Боди» — что бы это ни значило — стоит обработать возможную ошибку, а не прокидывать её в наш замечательный метод.</p><p>Например так:</p><figure><img src="https://media.tproger.ru/uploads/2023/08/ce9b16bb-d356-4be4-9a3b-6b3b1b93028d.png" alt="" /></figure><p>Кроме того, validatedRequest содержит в себе validatePsRequest.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/9071cf4b-79d8-4cfa-8fdf-c50f5a4d9027.png" alt="" /></figure><p>Но валидации для PsGetBalanceRequest тут не место. Она должна находиться в умном конструкторе PsGetBalanceRequest. Да и саму функцию validatePsRequest (в 24 строки) можно описать элегантней.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/adb03b5f-e5b4-485d-9ab2-db9eb9cd57d7.png" alt="" /></figure><p>HttpResponseEnvelope</p><p>Вопрос к nullable-полям. Почему в запросе перекладчика могут отсутствовать:</p><ul><li>method;</li><li>service;</li><li>operation;</li><li>body;</li><li>headers.</li></ul><p>Нужно чётко описать контракт в README и вместе с аналитиком прояснить, в каких случаях эти параметры могут быть null. Часто можно прийти к выводу, что поля класса сделаны nullable «на всякий случай» потому что лень писать качественно.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/114bf0e1-dfc4-4893-89c5-3cd3dcdcbc6a.png" alt="" /></figure><p>toJsonString</p><p>Функция не используется — удалить.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/06171f7e-ac83-4f0f-b29e-006477a3c702.png" alt="" /></figure><p>47 строка, emerge</p><p>Это просто мэпер, поэтому смарт-конструктор тут не нужен. Например можно использовать of для такой задачи.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/89da4f72-ed71-4af9-9f53-e5beee5631ef.png" alt="" /></figure><p>И выглядеть это будет так:</p><figure><img src="https://media.tproger.ru/uploads/2023/08/b768745e-01e2-4a7c-8a35-9c8674095e7e.png" alt="" /></figure><p>62 строка, emerge</p><p>В этой функции:</p><ul><li>не нужно создавать клон функции на 47-ой строке и передавать на вход Result — стоит удалить метод.</li><li>нужен map вместо fold.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/8d24256b-7f33-456a-a136-73c10631c35f.png" alt="" /></figure><p>А ещё стоит читать документацию к классу Result на сайте Kotlin и использовать методы по назначению и на нужном уровне. Но с этим мы разберемся подробнее при рефакторинге.</p><p>86 строка emerge(correlationId: String, ex: Throwable):</p><p>Дружелюбный Alt + F7 показал, что HttpResponseEnvelope, MqResponseMessage, RestServiceRequest и PsGetBalanceRequest не используются — удаляем.</p><p>Мы работаем не с библиотекой, так что IDE подсказывает, что нужно убрать, через warning. Можно еще подключить плагины: SonarLint, SonarAnalyzer и так далее — они хорошо помогают.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/9a4996f9-34cd-42b4-a7b2-01008eaab1e9.png" alt="" /></figure><p>Кстати, есть хорошая рекомендация от Марка Симана (кинга «Код, который умещается в голове»): делайте предупреждения ошибками и не допускайте пул реквесты с warning в коде.</p><h4>Пакет service</h4><p>MqHttpService</p><p>Метод registerJmsListener создает слушатель на очередь, когда мы поднимаем конфигурацию в классе MqServiceConfiguration. Описать слушатель на конкретной задаче лучше явно. Это упростит восприятие и сократит конфигурационную обертку.</p><p>При рефакторинге мы явно создадим слушатель в так называемом Buble Context — и покажем, как просто протестировать этот код.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/baac4f55-2eb0-43e7-b542-4354bd2b65cf.png" alt="" /></figure><p>MqPublisher</p><p>Содержит два небезопасных метода, которые могут выкинуть исключения:</p><ul><li>Первый — асинхронный метод, который оборачивает отправку сообщения в очередь на строке 22 в mono. Вызов может выкинуть исключение, как минимум, в двух очевидных случаях: если очереди не существует или отвалилось подключение к MQ.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/0031986e-ddbd-431c-b5ed-3a0899814abe.png" alt="" /></figure><figure><img src="https://media.tproger.ru/uploads/2023/08/13b9e0f6-e5ab-4317-a1fe-8a2df68f694e.png" alt="" /></figure><ul><li>Второй — небезопасный синхронный метод отправки сообщения в очередь.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/51bad80e-916b-4c02-a136-b56586bb556e.png" alt="" /></figure><p>Зачем нам вообще и асинхронный, и синхронный методы, когда можно завернуть IO-операцию в асинхронный стек реактивщины и оставить только publishToMq, переименовав метод в publish.</p><p>Функция fillMessageWithHeaders</p><p>Содержит бизнес-логику обогащения запроса заголовками. Такое сложно тестировать. Так что функцию следует вытащить в доменный объект и тестировать явно юнит-тестом.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/3f80fcad-1462-472a-bea4-8ad479e1c734.png" alt="" /></figure><p>Интерфейс PipelineService</p><p>Функция receiveMessage описывает громоздкий контракт, которому должны соответствовать сервисы обработки разных бизнес-потоков для разных слушателей. Если нужно передать много параметров — лучше оберните параметры в класс, получится проще и «чище».</p><p>Кроме того, большинство параметров передать инъекцией. Попробуйте написать Rest Controller, который описывает API URL-ресурса, и в сигнатуру метода пердеайте конфигурацию rest-сервиса, rest client, который понадобится под капотом на уровне application service, и mq publisher на всякий случай.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/6645f62b-082a-4117-a748-c56e43ab8443.png" alt="" /></figure><p>PipelineServicePS-сервис приложения</p><p>Application — сервис, помеченный аннотацией фреймворка как компонент. Если имплементировать интерфейс, начинаются очень странные дела в теле функции (честно, мы ее не придумали).</p><p>Входные параметры присваиваются полям класса, потому что разработчик так передал компоненты, конфигурацию и REST-клиент. Он логирует заголовки и тело запроса и пробрасывает входной параметр message в метод process.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/25bac1b4-c435-413f-a3a8-f4245d21bec6.png" alt="" /></figure><p>А ещё код как бы поощряет невалидные ситуации. Он написан в ущерб тестам и может сломать контракт.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/a8514d65-dff9-4647-a1f4-a023a82830cf.png" alt="" /></figure><p>Пройдёмся по ошибкам в классе:</p><ul><li>processMessage. Метод на 30 строк, который состоит из секции настройки переменных (больше похожей на ловушки). Разберёмся с аналитиком по контракту и упростим.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/7c6cb93f-af3a-44c3-942c-8eb04d843f0f.png" alt="" /></figure><ul><li>correlationId. 64-ая строка Устанавливается Elvis operator рандомно и при этом отсутствует в заголовке. На деле correlationId — должен передаваться в заголовке всегда, иначе система выдаст ошибку и сообщит об этом. Подробнее можно почитать тут.</li></ul><ul><li>url. Глушим отсутствие в заголовке значения метода. Так делать нельзя. Нужно проверить контракт, и если url не задан — вернуть сообщение об ошибке.</li></ul><ul><li>Вместо того чтобы писать комментарий ниже, лучше сразу найти способ или создать тикет и добавить его в комментарий — задача будет реализована в рамках 20-30% техдолга.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/73d24956-db9e-4489-939b-7fd9ae0618ec.png" alt="" /></figure><ul><li>httpReq должна быть неизменной — val.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/b99e452f-f7ca-44c3-8d51-89344052e00d.png" alt="" /></figure><p>Функция acceptedMqReceipt</p><p>Во-первых, нужно уменьшить количество входных параметров.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/f5a3f390-2314-46ca-8466-260f7ea0edf0.png" alt="" /></figure><p>Во-вторых, в строке 106 — вместо fold можно применить map.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/c1bfb9ed-aba5-453d-a27c-c4a36ca882c2.png" alt="" /></figure><p>В третьих, прочитаем тело функции:</p><figure><img src="https://media.tproger.ru/uploads/2023/08/36c408ed-f8c8-49e9-82e2-0b6313a62286.png" alt="" /></figure><ul><li>Строка 107. Если на вход пришел успешный запрос, вызываем функцию createMessageForReceipt, входных параметров в которую слишком много.</li><li>Строка 109. Вызываем map в котором лежит бизнес-логика преобразования сообщения, созданного в функции createMessageForReceipt.</li><li>Строка 112. Отправляем ответ на что-то неизвестное, судя по имени jmsResponse.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/c15b75fd-5cba-4a98-9ffa-8dec6344a6b2.png" alt="" /></figure><ul><li>Строка 100. Если приходит успешный HttpRequestEnvelope, который был собран из строки 75 и провалидирован, берем некое Ps, помещаем в HttpRequestEnvelope и проваливаемся в странную функцию acceptedMqReceipt, где создаем сообщение для квитанции, превращаем его в jmsResponse и отправляем в строку 112</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/b56c6d0d-5ac1-4731-ae9f-446e807b2f49.png" alt="" /></figure><p>То есть функция должна отправить сообщение с квитанцией в очередь mq. Но из имени не понятно, что делается в теле. И происходящее описано очень сложно. Решение — переименовать функцию, отправить квитанцию на вход, подать один параметр, в теле отправить квитанцию в mq.</p><p>Функция fun createMessageForReceipt</p><p>Эта функция буквально говорит то же, что и предыдущая: «Я содержу много бизнес-логики преобразований. Меня неудобно читать. Во мне много входных параметров. Во мне неверно используют Result».</p><p>Кроме того, не стоит кидать fold в fold через map. А map{it} мэпит в самого себя строка 151.</p><p>createMessageForReceipt после упрощения будет возвращать объект, который легко протестировать.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/27d84f76-ad9e-45f6-ae57-d457921ac147.png" alt="" /></figure><p>Функция convertToReceiptJmsResponse</p><p>Во-первых, mqConfig передавать не нужно, это — параметр класса, его класс получает в виде инъекции. Во-вторых, бизнес-логику конвертации стоит выносить из сервиса в класс, так как в данном приватном методе её не протестировать наглядно и просто.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/d2e42717-ee3e-4cb9-9971-992ec9f00ca2.png" alt="" /></figure><p>Функция convertToBasicJmsResponse</p><p>Те же замечания что и к предыдущей функции.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/72b685fd-9416-40ef-991f-fa8194890775.png" alt="" /></figure><p>Функция prepareSipHeaders</p><p>Те же замечания что и к предыдущей функции.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/ee64cc7c-42bc-483f-9909-dd0ae7edbb03.png" alt="" /></figure><p>Функция processMqResponse</p><p>В HttpResponseEnvelope.emerge нет умного конструктора, поэтому функцию назвать нужно было map и использовать просто в of.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/e3d4e8a3-e4ed-44a1-852e-7ed64eafd935.png" alt="" /></figure><p>Кроме того, нужно правильно применить Result в классе HttpResponseEnvelope. Сейчас, этот метод (функция) возвращает Result просто потому что.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/f93cbfb8-a100-40a8-b122-9bbc362a11fb.png" alt="" /></figure><p>Функция submitMqReceipt</p><p>Судя по развилке, функция делает несколько дел сразу.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/f7272bdf-0026-4b8b-962f-873898fd4ff1.png" alt="" /></figure><ul><li>Если на вход залетает mqResponse — Result&lt;MqResponseMessage&gt; в виде Result.success — то функция некрасиво создает квитанцию createMessageForReceipt. И через две операции мэпит в JMS ответ и отправляет в MQ.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/c1dca4e7-81d8-4850-bd99-01b949323d5a.png" alt="" /></figure><ul><li>Если на вход (строка 204) залетает mqResponse — Result&lt;MqResponseMessage&gt; в виде Result.failure — то создаётся createMessageForReceipt квитанции об ошибке.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/08/5e4cd3e3-4c7c-4bd1-8070-86586b055aea.png" alt="" /></figure><p>К тому же функция submitMqReceipt принимает на вход слишком много лишних параметров.</p><p>Функция submitMqResponse чуть проще, но ошибки те же: выбор, конвертация, отправка.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/642ae0e2-1e45-4585-8ac8-26bb694432a2.png" alt="" /></figure><p>После форматирования чуть проще воспринять.</p><figure><img src="https://media.tproger.ru/uploads/2023/08/d389859c-0dc9-4ead-bd61-d1b68a7c02aa.png" alt="" /></figure><p>В следующей части начнём рефакторить код и разбирать всё подробнее.</p>]]></content:encoded>
    </item>
    <item>
      <title>Принципы SOLID на примерах Python</title>
      <link>https://tproger.ru/articles/principy-solid-python</link>
      <comments>https://tproger.ru/articles/principy-solid-python?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Марина Александровна]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/principy-solid-python</guid>
      <description><![CDATA[<p>Принципы SOLID на примерах Python-кода, с подробным объяснением преимуществ и возможных недостатков каждого принципа.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/principy-solid-python">Принципы SOLID на примерах Python</a>»</p>]]></description>
      <category><![CDATA[Для начинающих]]></category>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Основные принципы программирования]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 09 Jul 2023 11:02:27 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вероятно, вы не раз слышали о так называемых SOLID принципах. Но что на самом деле означает каждый из принципов SOLID и как правильно применять их на практике? Вы найдёте ответы в данной статье.</p><h2>Что такое SOLID принципы?</h2><p>Современная разработка требует передовых навыков и глубоких знаний сложных принципов программирования. Одной из наиболее важных структур, используемых сегодня в разработке программного обеспечения, являются принципы SOLID — они же пять основных принципов объектно-ориентированного программирования.</p><p>Понимание SOLID является обязательным для всех разработчиков, и в этой статье мы объясним их простым языком.</p><h2>Расшифровка SOLID</h2><p>Аббревиатура SOLID включает в себя название пяти принципов «хорошего дизайна», когда речь идет о разработке программного обеспечения. Акроним SOLID означает следующие принципы:</p><ol><li>Принцип единственной ответственности (Single Responsibility Principle – SRP): Каждый класс должен иметь только одну причину для изменения. Это означает, что класс должен быть ответственным только за одну конкретную функцию или задачу. Этот принцип помогает сделать классы более связанными, легко понятными и поддерживаемыми.</li><li>Принцип открытости/закрытости (Open/Closed Principle – OCP): Программные сущности, такие как классы, модули и функции, должны быть открыты для расширения, но закрыты для модификации. Вместо изменения существующего кода, следует добавлять новый код для внесения изменений. Это позволяет создавать более стабильные и гибкие системы.</li><li>Принцип подстановки Лисков (Liskov Substitution Principle – LSP): Объекты в программе должны быть заменяемыми экземплярами их базовых типов, не нарушая корректность программы. Это означает, что код, который работает с базовым типом, должен работать и с любым его подтипом, не вызывая ошибок или неожиданного поведения. Этот принцип обеспечивает согласованность в использовании наследования и полиморфизма.</li><li>Принцип разделения интерфейса (Interface Segregation Principle – ISP): Клиенты не должны зависеть от интерфейсов, которые они не используют. Вместо создания общих интерфейсов следует создавать специфические интерфейсы, предназначенные для конкретных клиентов. Это позволяет избежать излишней связности между компонентами системы и улучшить модульность.</li><li>Принцип инверсии зависимостей (Dependency Inversion Principle – DIP): Классы должны зависеть от абстракций, а не от конкретных реализаций. Высокоуровневые модули не должны зависеть от низкоуровневых модулей. Оба типа модулей должны зависеть от абстракций. Этот принцип помогает уменьшить связанность между компонентами системы и повысить их переиспользуемость.</li></ol><p>Все эти пять принципов SOLID способствуют созданию гибкого и легко поддерживаемого кода, а также улучшают модульность, переиспользуемость и расширяемость системы.</p><p>Акроним SOLID принципов был сформулирован Робертом С. Мартином, также известным как «Дядя Боб», одним из самых влиятельных разработчиков программного обеспечения в области разработки программного обеспечения.</p><p>Хотя многим эти принципы могут показаться сложными, мы постараемся объяснить их простым и понятным способом.</p><h2>Принцип единой ответственности</h2><p>Мы начнем с принципа единой ответственности, который гласит, что каждый класс или компонент кода должен иметь только одну ответственность. Это также означает, что у него должна быть только одна причина для изменения. Чтобы проиллюстрировать принцип, давайте рассмотрим пример.</p><p>Предположим, у нас есть файл с именем «автомобиль» с кодом, отвечающим за эксплуатацию, управление, контроль и мониторинг автомобиля. В соответствии с принципом единой ответственности каждая из этих задач должна быть разделена на отдельный файл или компонент. Также стоит отметить, что SRP применяется не только к классам, но и к функциям и методам.</p><h3>SOLID примеры на Python — принцип единой ответственности</h3><p>Концепция принципа единой ответственности (SRP) заключается в том, что класс должен иметь только одну причину для изменения. Давайте рассмотрим пример с классом User, который нарушает этот принцип:</p><p>В этом примере класс User имеет несколько ответственностей. Он отвечает за сохранение пользователя в базе данных, отправку электронной почты и генерацию отчета. Как результат, класс становится сильно связанным и трудным для понимания и поддержки.</p><p>Давайте применим принцип единой ответственности, разделив класс User на несколько отдельных классов с единой ответственностью:</p><p>Теперь каждый класс имеет только одну ответственность. Класс User отвечает только за данные пользователя и сохранение их в базе данных. Класс EmailSender занимается отправкой электронной почты, а класс ReportGenerator отвечает за генерацию отчета пользователя.</p><p>Это пример применения принципа единой ответственности, который помогает разделить функциональность на более мелкие и специализированные классы, упрощает понимание кода и делает его более гибким для изменений.</p><h3>Визуальное представление SRP</h3><p>Визуализация SRP, как одного из SOLID принципов, может быть представлена в виде диаграммы классов, где каждый класс имеет только одну ответственность. Каждый класс должен быть ответственным только за выполнение одной конкретной функции или задачи. Если класс имеет несколько ответственностей, это может указывать на нарушение SRP:</p><p>В этой визуализации каждый класс представлен прямоугольником, и у каждого класса есть только свои собственные методы, относящиеся только к его ответственности.</p><p>Важно отметить, что это просто концептуальное представление, и сама диаграмма может быть намного более сложной в реальном приложении. Однако основная идея заключается в том, чтобы иметь ясное представление о том, что каждый класс имеет только одну ответственность, и у классов нет перекрестных зависимостей между своими ответственностями.</p><h3>Резюмируя</h3><p>Принцип единой ответственности требует, чтобы классы имели только одну ответственность. Это может показаться простым, но это одна из самых важных концепций, которые необходимо понимать при разработке высококачественного программного обеспечения. Идея в том, что каждая часть кода должна отвечать только за одну задачу. Например, у класса должны быть только связанные методы и переменные для его конкретной задачи, и ничего больше.</p><h2>Принцип открытого-закрытого</h2><p>Теперь рассмотрим принцип открытого-закрытого. Он предполагает, что программные компоненты должны быть открыты для расширения и закрыты для любых модификаций. Другими словами, когда создаются новые функции, код следует расширять, не затрагивая его существующей структуры. Для этого разработчики могут использовать комбинацию наследования и полиморфизма. Важным преимуществом соблюдения принципа «открыто-закрыто» является снижение риска непредвиденных ошибок при внесении изменений.</p><h3>SOLID примеры на Python — принцип открытого-закрытого</h3><p>Принцип открытого-закрытого (Open/Closed Principle – OCP) заключается в том, что программные сущности, такие как классы, модули и функции, должны быть открыты для расширения, но закрыты для модификации. Давайте рассмотрим пример с классами Shape и AreaCalculator, где будем применять принцип OCP:</p><p>В этом примере у нас есть абстрактный класс Shape, который определяет метод calculate_area(). Затем у нас есть два класса-наследника Rectangle и Circle, которые реализуют этот метод для расчета площади прямоугольника и круга соответственно.</p><p>Класс AreaCalculator отвечает за вычисление общей площади для набора фигур. Он использует SOLID принцип OCP, поскольку он открыт для расширения новыми типами фигур, но закрыт для модификации своей основной логики. Если мы хотим добавить новый тип фигуры, например, треугольник, мы можем создать новый класс Triangle, реализующий метод calculate_area(), и передать его в AreaCalculator.calculate_total_area() без изменения самого AreaCalculator:</p><p>Таким образом, принцип открытого-закрытого позволяет нам добавлять новые типы фигур, расширяя функциональность, без изменения существующего кода в AreaCalculator. Это делает код более гибким и устойчивым к изменениям.</p><h3>Резюмируя</h3><p>Принцип открытого-закрытого гласит, что классы должны быть открыты для расширения, но закрыты для модификации. Расширение класса не должно требовать модификации существующего кода. Это гарантирует, что код может быть изменен и расширен с минимальным нарушением остальной части кодовой базы.</p><h2>Принцип подстановки Барбары Лисков</h2><p>Принцип подстановки Лисков — еще одна важная концепция. Он предполагает, что объекты подклассов должны быть полностью взаимозаменяемы с объектами своих родительских классов, т. е. любой дочерний класс должен вести себя точно так же, как родительский класс. Это также известно как «полиморфизм подтипов» и помогает разработчикам создавать более управляемый и надежный код.</p><h3>SOLID примеры на Python — принцип подстановки Барбары Лисков</h3><p>Принцип подстановки Барбары Лисков (Liskov Substitution Principle – LSP) гласит, что объекты должны быть заменяемыми экземплярами их базовых типов без нарушения корректности программы. Давайте рассмотрим пример с классами Rectangle (Прямоугольник) и Square (Квадрат), чтобы проиллюстрировать принцип LSP:</p><p>В этом примере класс Rectangle представляет прямоугольник с методами для установки ширины и высоты, а также для получения площади.</p><p>Класс Square наследуется от Rectangle и переопределяет методы set_width() и set_height(). В случае квадрата ширина и высота всегда должны быть одинаковыми, поэтому при установке одного измерения класс Square автоматически устанавливает и другое измерение равным ему.</p><p>Однако данный пример нарушает SOLID принцип LSP. Рассмотрим следующий код:</p><p>В этом примере мы вызываем функцию print_area(), которая ожидает объект типа Rectangle. Когда мы передаем rectangle, результат площади правильный (20), потому что ширина и высота были установлены независимо. Однако, когда мы передаем square, ожидаемая площадь должна быть также 20, но фактически получаем 16. Это происходит из-за изменения поведения методов set_width() и set_height() в классе Square.</p><p>Таким образом, класс Square не является полностью заменяемым объектом для класса Rectangle, нарушая принцип LSP. Чтобы исправить это, мы можем пересмотреть дизайн иерархии классов, чтобы избежать нарушения принципа LSP, или использовать интерфейсы и абстракции для достижения корректного поведения при замене объектов.</p><h3>Резюмируя</h3><p>Принцип подстановки Лисков гласит, что любой экземпляр подтипа должен иметь возможность заменить экземпляр своего базового типа. Это означает, что подтипы не должны нарушать поведение своих базовых типов. По сути, базовый класс и любые подклассы должны быть взаимозаменяемыми без нарушения кода.</p><h2>Принцип разделения интерфейса</h2><p>Принцип разделения интерфейса довольно прост и гласит, что разработчики не должны полагаться на огромные классы с множеством методов. Вместо этого разработчикам следует создавать множество меньших классов с меньшим количеством методов. Это помогает уменьшить сложность кода и упростить управление программой.</p><h3>SOLID примеры на Python — принцип разделения интерфейса</h3><p>Принцип разделения интерфейса (Interface Segregation Principle – ISP) гласит, что клиенты не должны зависеть от интерфейсов, которые они не используют. Вместо создания общих интерфейсов следует создавать специфические интерфейсы для конкретных клиентов. Давайте рассмотрим пример с интерфейсами для различных устройств вывода ввода:</p><p>В этом примере у нас есть абстрактные классы InputDevice и OutputDevice, представляющие интерфейсы для устройств ввода и вывода соответственно. Затем мы определяем конкретные классы Keyboard, Mouse, Monitor и Printer, которые реализуют соответствующие методы.</p><p>Применяя SOLID  принцип ISP, мы разделяем интерфейсы на более специфические, чтобы клиенты могли зависеть только от интерфейсов, которые они используют. Например, если клиенту нужен только ввод с клавиатуры, он может зависеть только от интерфейса InputDevice и использовать класс Keyboard:</p><p>В этом примере функция process_input() принимает объект, реализующий интерфейс InputDevice, и обрабатывает его ввод. Здесь мы передаем объект Keyboard, который соответствует интерфейсу InputDevice. Таким образом, клиент зависит только от необходимого интерфейса и не зависит от лишних методов или классов.</p><p>Таким образом, принцип разделения интерфейса позволяет создавать более специфические интерфейсы для клиентов, избегая излишней связанности и улучшая модульность и переиспользуемость кода.</p><h3>Резюмируя</h3><p>Принцип разделения интерфейса гласит, что классы не должны зависеть от методов, которые они не используют. Это помогает улучшить читаемость кода, а также обеспечить сильную связность. Другими словами, классы, которые зависят от других классов, не должны зависеть больше, чем им нужно для выполнения своей работы.</p><h2>Принцип инверсии зависимостей</h2><p>Наконец, у нас есть принцип инверсии зависимостей, который предполагает, что классы не должны напрямую полагаться на другие классы, а вместо этого должны зависеть от абстракций. Это помогает значительно снизить сложность кода и сделать программу менее подверженной ошибкам.</p><h3>SOLID примеры на Python — принцип инверсии зависимостей</h3><p>Принцип инверсии зависимостей (Dependency Inversion Principle – DIP) гласит, что классы должны зависеть от абстракций, а не от конкретных реализаций. Высокоуровневые модули не должны зависеть от низкоуровневых модулей. Оба типа модулей должны зависеть от абстракций. Давайте рассмотрим пример с классами Notification и EmailSender, чтобы проиллюстрировать принцип DIP:</p><p>В этом примере класс User зависит от конкретной реализации EmailSender в качестве сервиса уведомлений. Это создает прямую связь между User и EmailSender, что делает классы сложнее для тестирования и внесения изменений.</p><p>Чтобы применить SOLID  принцип DIP, мы изменяем User, чтобы он зависел от абстракции Notification, а не от конкретной реализации:</p><p>Теперь User принимает объект notification_service, реализующий интерфейс Notification, через конструктор. Это позволяет передавать различные реализации уведомлений, такие как EmailSender или SMSNotification, без изменения самого User:</p><p>Теперь User зависит от абстракции Notification и может быть легко настроен для работы с различными реализациями уведомлений. Это уменьшает связанность между классами, делает их более гибкими и легкими для тестирования и модификации.</p><h3>Резюмируя</h3><p>Принцип инверсии зависимостей гласит, что код должен зависеть от абстракций, а не от конкретики. Это означает, что код не должен зависеть от конкретных реализаций, а скорее от абстракции, которая затем реализуется. Это помогает повысить гибкость и повторное использование кода.</p><h2>Заключение</h2><p>Принципы SOLID важны для понимания любого разработчика программного обеспечения. Важно помнить, что эти принципы следует применять при разработке программного обеспечения для достижения наилучших результатов. SOLID принципы могут помочь обеспечить простоту сопровождения и расширения кода в будущем. Хорошо понимая SOLID принципы, вы сможете писать код, который будет лучше структурирован, прост в масштабировании и обслуживании.</p><figure><img src="https://media.tproger.ru/uploads/2023/07/a8e7718b-d42f-4a59-bb21-84853c285ccb.jpg" alt="" /></figure><p>Примечание Рейтинг степени важности в разработке (0-10) является относительной оценкой и может различаться в зависимости от конкретной ситуации и контекста разработки.</p><p>Надеемся, что принципы SOLID на Python примерах оказались полезны и доступны для понимания.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как написать чистый код и сделать жизнь проще</title>
      <link>https://tproger.ru/articles/kak-napisat-chistyj-kod-i-sdelat-zhizn-proshhe</link>
      <comments>https://tproger.ru/articles/kak-napisat-chistyj-kod-i-sdelat-zhizn-proshhe?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Мария Кривоченко]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kak-napisat-chistyj-kod-i-sdelat-zhizn-proshhe</guid>
      <description><![CDATA[<p>Рассказали, что такое чистый код и зачем он нужен и опишем принципы его создания. Советы основаны на книгах по теме и личной практике.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kak-napisat-chistyj-kod-i-sdelat-zhizn-proshhe">Как написать чистый код и сделать жизнь проще</a>»</p>]]></description>
      <category><![CDATA[Практика]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 30 Jun 2023 09:50:54 GMT</pubDate>
      <content:encoded><![CDATA[<p>В университетах, как правило, рассказывают базовые понятия: алгоритмы и структуры данных, вычислительную математику. Но не про то, как красиво спроектировать приложение и сделать код удобочитаемым и пригодным для доработок. В итоге на практике мы часто получаем бессистемный подход и нечто, что трудно читать, сложно и страшно рефакторить. Потому что явно что-то где-то да упадёт.</p><figure><img src="https://media.tproger.ru/uploads/2023/06/701350fb-d82d-4b85-ab76-64c5584234c5-autoconverted.jpeg" alt="" /></figure><p>Чтобы не допускать такого, мы запускаем серию статей про код, где подробно расскажем, как писать красиво и чисто и получать на выходе поддерживаемый код. В первой части расскажем, что такое чистый код и зачем он нужен и опишем принципы его создания. А дальше на конкретных примерах разберём, как делать надо и не надо.</p><ol><li><a href="https://tproger.ru/#part1">Да кому вообще нужен этот чистый код?</a></li><li><a href="https://tproger.ru/#part2">Как написать чистый код?</a></li><li><a href="https://tproger.ru/#part3">Подробное руководство</a></li><li><a href="https://tproger.ru/#part4">Как править НЕ чистый код?</a></li></ol><h2>Да кому вообще нужен этот чистый код?</h2><p>Читаемый, легко тестируемый, легко компонуемый код, который решает бизнес-задачу и сам является документацией, сокращает ТТM (time to market). За счёт времени, которое разработчик тратит на изучение приложения, внесение изменений в код, добавление новых фич и прочее.</p><p>И если приложение плохо спроектировано, код спутан — продуктивность команды, которой приходится разбираться с этим примерно 70% рабочего времени, падает. Это факт. И я с ним сталкивался.</p><p>Так что, по сути, он нужен всем, кто работает в IT.</p><h3>Разработчикам</h3><p>В первую очередь для того, чтобы быстро анализировать и дорабатывать уже готовый код — в том числе собственный, написанный два месяца назад и благополучно за это время забытый. Так, сокращаем TTM.</p><p>К тому же, если мы плохо проектируем, плохо пишем, плохо автоматизируем, плохо доставляем, структура начинает тормозить, возникают ошибки. И приходят бизнес, лиды, тестировщики с претензиями, баг-репортами и доработками.</p><p>Чистый и юзабельный код не ценность — а обязанность. И чем он однообразнее, скучнее и проще тем проще нам автоматизировать процессы.</p><h3>Лидам</h3><p>Лиды, как и разработчики, отвечают за качество готового продукта. И им чистый код помогает быстрее проводить ревью, переключаться между задачами и следить за соблюдением соглашений.</p><p>Бегите из команды, если ваш техлид говорит: «Чистый код — это миф. А те, кто пишут про него книги, статьи и доклады, не работают, а выдумывают теорию, которая на практике неприменима».</p><h3>Бизнесу</h3><p>Бизнесу доклады про эстетику и красоту кода неинтересны. Бизнесу важна скорость появления фичей и отсутствие багов.</p><p>Так что чем быстрее работает команда, чем больше качественных продуктов и доработок она выпускает, тем больше бизнес может заработать.</p><h2>Хорошо, и как тогда написать чистый код?</h2><h3>Сперва почитать хорошие книги</h3><p>Да, лень — но надо. Всё хорошее и «правильное» уже придумано, и чтобы писать код грамотно, необязательно 5 лет изобретать велосипеды, как это делал я.</p><p>Рекомендую читать Роберта Мартина, Владимира Хорикова, Джошуа Блоха, Скота Влашина, Стива Макконнелла и стремиться к профессиональной простоте кодирования. Важно: старайтесь читать книги в оригинале.</p><p>Важно понимать, что некоторые топики, которые тот же Мартин отстаивал в 2008, уже не актуальны. Советую отсеивать их и просто забирать полезное.</p><h3>Обратить внимание на принципы Unix</h3><ul><li>Write programs that do one thing and do it well — Каждая программа, класс, функция выполняет одну задачу — и выполняет хорошо.</li><li>Write programs to work together — Программы работают совместно, и мы можем выстроить пайплайн. Компоненты на разных уровнях работают вместе и взаимодействуют посредством классов.</li><li>Write programs to handle text streams, because that is a universal interface — Программы взаимодействуют, используя универсальный текстовый интерфейс, а классы — код. Тип — универсальный интерфейс взаимодействия функций и классов.</li></ul><p>Эти принципы помогают мне осознавать архитектуру в срезе сложного приложения, и проектировать программы/классы/функции.</p><p>Unix-философия хорошо подходит для проектирования микросервисов (программ). Поэтому я рекомендую исследовать мир Unix и использовать Linux разработчикам.</p><h3>Внимательно прочитать руководство ниже</h3><p>Здесь я привёл основные рекомендации по написанию чистого кода, основанные на многих годах практики и книгах «Чистый код» и «Чистая архитектура: Руководство ремесленника по структуре и проектированию программного обеспечения».</p><figure><img src="https://media.tproger.ru/uploads/2023/06/aa3fb774-6e89-49f4-b947-69e6160a7164-autoconverted.jpeg" alt="" /></figure><h4>Всегда улучшайте код, с которым работаете</h4><p>Мартин использует «Правило бойскаута» и призывает улучшать кодовую базу постоянно (так же, как бойскауты оставляют кемпинг в лучшем состоянии, чем он был до визита). Это очень важная жизненная идея.</p><p>Поддерживайте свой код в хорошем состоянии. Исправляйте ошибки как можно раньше, удаляйте неиспользуемый код и обновляйте его, чтобы он соответствовал новым требованиям.</p><p>И если нашли «сомнительную, странную дичь» в старом коде — соберите команду, обсудите то, что обнаружили. Вероятно, это «нечто» именно то, что стоит улучшить — или вовсе избавиться.</p><h4>Think twice, code once</h4><p>Прежде чем приступить к разработке, доработке, рефакторингу, разберитесь в том, как функция/система работает по данному потоку. Станьте экспертом в предметной области.</p><h4>Используйте функциональную парадигму</h4><p>Она загоняет нас в рамки чистого кода и способствует следовать лучшим практикам. Кроме того, чистое ООП в программах не нужно. Серьёзно, нет.</p><p>Это тема для отдельной статьи, но тут поделюсь фактом: в нескольких больших банках функциональная парадигма — стандарт де-факто при написании микросервисов. И советую почитать, что Роберт Мартин пишет в блогеAnd the future is looking very functional to me.</p><h4>Делите код на слои</h4><p>Каждый слой должен выполнять определённую функцию и быть независимым от других слоёв.</p><ul><li>Разделяйте логику и представление, чтобы упростить тестирование и обеспечить независимость компонентов.</li><li>Используйте Вертикальное разделение. «Переменные, функции должны быть определены близко к тому месту, где они используются» (G10 Vertical Separation Clean Code, page 292).</li><li>Опирайтесь на луковичную архитектуру, где внутренний слой ничего не знает о внешнем — это помогает визуализировать и проектировать структуру приложения.</li></ul><figure><img src="https://media.tproger.ru/uploads/2023/06/9a4a3467-f6ad-4fd8-8e6b-13842056d9e0-autoconverted.jpeg" alt="" /></figure><p>Пара материалов по теме:</p><ul><li><a href="https://www.redhat.com/architect/5-essential-patterns-software-architecture#layered">https://www.redhat.com/architect/5-essential-patterns-software-architecture#layered</a></li></ul><ul><li><a href="https://www.redhat.com/architect/14-software-architecture-patterns">https://www.redhat.com/architect/14-software-architecture-patterns</a></li></ul><ul><li><a href="https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/">https://jeffreypalermo.com/2008/07/the-onion-architecture-part-1/</a></li></ul><h4>Соблюдайте принципы SOLID для проектирования сервисов, классов и функций</h4><p>Нам особенно важен «Принцип единственной ответственности (Single Responsibility Principle)» для классов и функций, сервисов:</p><p>«Каждый класс или функция должны выполнять только одну задачу».</p><p>То есть пишите функции, которые делают только одну вещь — и делают её хорошо. Есть несколько способов убедиться, что выполнили это правило:</p><ul><li>функция выполняет только те действия, которые находятся на одном уровне с объявленным именем, выполняет задачу как бы замкнуто в своём теле; и если какие-то запросы пролетают наружу, как это бывает в функциях сервисов приложений, то через шлюзы;</li><li>функция выполняет действия, которые находятся на одном уровне абстракции;</li><li>из одной функции не получается выделить другие;</li><li>функцию не получается разделить на секции.</li></ul><h4>Избегайте наследования</h4><p>Сложные иерархии наследования приводят к путанице и проблемам отладки. Постарайтесь ограничить сложность класса, сохраняя связанную функциональность вместе, а не распределяя её по нескольким уровням абстракции.</p><h4>Предпочитайте полиморфизм операторам If/Else</h4><p>Приложение будет более гибким, если мы вынесем поведение в классы, убрав тем самым бизнес логику принятия решений, ветвлений в родственные доменные классы.</p><h4>DRY</h4><p>Код должен быть повторно использован только тогда, когда имеет ту же ответственность.</p><p>Если вы используете один и тот же код несколько раз, выносите его в отдельную функцию (класс, компонент, сервис) чтобы избежать дублирования и упростить поддержку.</p><p>Не увлекайтесь DRY при написании тестов. Универсальность в них часто уменьшает читабельность, а значит, ясность. Помните, тест — это документация.</p><h4>Классы не должны знать о внутренней реализации других классов</h4><p>Не думайте о внутренней работе юнита (класса, функции) — лучше смотреть на него, как на чёрный ящик. Это поможет при проектировании и писании прекрасно тестируемого кода.</p><h4>G22: Make Logical Dependencies Physical</h4><p>Если один модуль зависит от другого, эта зависимость должна быть физической, а не только логической. Также зависимость должна быть очевидной</p><h4>Используйте понятные и описательные имена</h4><p>Для всего: переменных, функций, классов и других элементов кода. Избегайте сокращений и аббревиатур.</p><h4>Форматируйте код</h4><p>Казалось бы, очевидное правило. Но как показывает практика — нет. Поэтому:</p><ul><li>открыли класс в Idea, нажали Ctrl + Alt + L, продолжаем работу;</li><li>одной пустой строки в отступах между блоками в коде достаточно.</li></ul><p>Современные IDE умеют форматировать перед коммитом или при сохранении файла. Но лучше один раз вручную установить стандарт — и потом придерживаться его средствами автоматизации.</p><h4>Опирайтесь на 3 закона TDD</h4><ul><li>You may not write production code until you have written a failing unit test — Мы не выпускаем в прод код, который не покрыт тестами.</li><li>You may not write more of a unit test than is sufﬁcient to fail, and not compiling is failing — Покрываем тестами код в достаточном количестве, чтобы убедиться, что данный слой (класс, функция) работает верно. Не дублируйте тест кейсы на разных уровнях. Если все сделали правильно: покрыли юнит-тестами бизнес-логику, не нужно дублировать проверку всех бизнес-кейсов интеграционными тестами.</li><li>You may not write more production code than is sufﬁcient to pass the currently failing test — Написал код — написал тесты. Или в обратном порядке.</li></ul><p>Эти законы можно интерпретировать и использовать и тем, кто не придерживается каноничного TDD.</p><h4>Не используйте исключения для обработки ошибок</h4><p>Мартин рекомендовал в своё время использовать исключения. А я — нет. Исключения для нас — только сигналы багов. А для обработки ошибок мы используем <a href="https://fsharpforfunandprofit.com/rop/">R.O.P.</a></p><h4>Комментарий — признак плохого кода</h4><p>Пишите комментарии внутри кода только тогда, когда они объясняют, не что код делает, а почему он написан таким образом.</p><p>Общее правило для большинства случаев: если вам приходится добавлять комментарии — перепишите код.</p><h4>Задавайте границы для внешних библиотек и систем</h4><p>Оборачивайте внешние библиотеки или API в API который подходит вашему дизайну — это даст запас прочности.</p><p>Применяйте<a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/anti-corruption-layer"> Anti-corruption Layer pattern</a> в дизайне кода. И используйте юнит-тесты.</p><h4>Живите в парадигме Null Safety</h4><p>Не передавайте null</p><p>Не возвращайте null</p><p>Не используйте null</p><h4>Размер функции или метода — максимум пять строк</h4><p>Об этом пишет Кристиан Клаусен в книге «Пять строк кода» которую Роберт Мартин рекомендует.</p><p>Сам Мартин указывает, что размер функции не должен превышать 20 строк по 150 символов каждый, но чем меньше — тем лучше.</p><h4>Идеальное количество входных параметров для функции — один</h4><p>Параметры усложняют функцию и запутывают её восприятие. Особенно это касается выходных — потому что мало кто ожидает, что функция в аргументах будет возвращать значения. Поэтому:</p><ul><li>функция не преобразует входной аргумент;если вы видите такую функцию или написали её только что, исправьте — результат изменения нужно передавать в возвращаемом значении — и точка;</li><li>некоторые аргументы стоит упаковать в отдельном классе — так советует старина Блох, отец Макконэл, советую я.</li></ul><h4>Отделите бизнес-логику предметной области от логики приложения</h4><p>Например, бизнес-правила, консистентное состояние объектов в системе, проверки ограничений (валидация), расчёты, используемые в решении, не следует путать с техническими деталями, такими как схема базы данных, интеграции с внешними системами, сервисами уровня приложений и уровнем DTO.</p><p>Наконец, помните, что написание чистого кода — это ремесло и где-то даже искусство, которое требует практики и терпения. Не бойтесь переписывать код, если это поможет улучшить его качество и поддерживаемость. А если ищете элегантное простое решение, смотрите <a href="https://habr.com/ru/companies/gazprombank/articles/722620/">кукбук</a>.</p><h4>А если мне попал в руки НЕ чистый код?</h4><p>Тогда начинаем рефакторинг. Я использую такой алгоритм:</p><ul><li>Просматриваем приложение сверху вниз, оцениваем насколько оно хорошо спроектировано и как описаны классы.</li><li>Узнаем у автора этого непотребства, что делает сервис.</li><li>Открываем Readme и в функциональном стиле описываем назначение сервиса. Нам пригодятся <a href="https://www.markdownguide.org/basic-syntax/">Markdown Best practices</a>. Лучше визуализировать процесс (например, с помощью <a href="https://excalidraw.com/">https://excalidraw.com/</a>) это поможет представить правильную картину и структуру классов.</li><li>Продумываем, как будем всё это тестировать. Если всё выглядит совсем монструозно, я предлагаю воспользоваться дорогими, но покрывающими максимум кода интеграционными тестами. Раз уж это творение орков работает, зафиксируем состояние и покроем крайние точки тестами. Проверенная тактика.</li><li>Во время рефакторинга сразу покрываем код юнит-тестами.</li></ul><p>На выходе получим простой элегантный дизайн в функциональной парадигме, которая способствует следованию хорошим принципам дизайна и рекомендациям Мартина в частности.</p><h4>А что дальше?</h4><p>В первой части я описал смысл и принципы чистого кода. В следующей мы проанализируем приложение на предмет чистоты кода. Я пройдусь по каждому классу и опишу недостатки. И расскажу, как это исправлять.</p>]]></content:encoded>
    </item>
    <item>
      <title>Код как у сеньора: рефакторинг</title>
      <link>https://tproger.ru/articles/kod-kak-u-senora-refaktoring</link>
      <comments>https://tproger.ru/articles/kod-kak-u-senora-refaktoring?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Кристина Дмитриевых]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/articles/kod-kak-u-senora-refaktoring</guid>
      <description><![CDATA[<p>Разбираемся, чем отличается настоящий рефакторинг от банального переписывания кода на примере книги Мартина Фаулера «Рефакторинг».</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/articles/kod-kak-u-senora-refaktoring">Код как у сеньора: рефакторинг</a>»</p>]]></description>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Гостевая публикация]]></category>
      <category><![CDATA[Статьи]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 07 Oct 2022 14:42:53 GMT</pubDate>
      <content:encoded><![CDATA[<p>Все мы знаем, что такое рефакторинг. Берешь невнятный кусок кода, выкидываешь и пишешь новый, быстрее, без багов… К сожалению, все не так просто. Давайте попробуем вместе разобраться, чем же отличается настоящий рефакторинг как практика от банального переписывания кода.</p><p>Это — первая серия проекта «Код Раковского», где Александр Раковский, Senior Java разработчик компании ITentika, расскажет о том, что считает важным и интересным в сфере программирования.</p><p>Каких-то жестких правил тут не будет, главное, запомните:</p><ol><li>Здесь не любят костыли и велосипеды.</li><li>Здесь не терпят код без тестов.</li><li>Здесь чтут отцов аджайла.</li><li>Здесь суровое экстремальное программирование.</li></ol><p>Возьмем пример из революционной книги Мартина Фаулера «Рефакторинг». Книге в следующем году 20 лет стукнет, поэтому пример оттуда сегодня будет смотреться необычно.</p><p>Этот небольшой проект, написанный на Java — программа для печати чека клиенту в видеопрокате. Исходники кода можно взять <a href="https://github.com/rakovi4/refactoring1stEdition">тут</a>.</p><p>Она выводит арендованные фильмы и стоимость их аренды и рассчитывает общую сумму, которую должен клиент, при этом в чек выводятся баллы программы лояльности. Проект специально наполнен кучей огрехов, которые в книге называются Code Smell — так имитируется кусок кода типичного корпоративного приложения. В рамках примера нам надо будет добавить пару фич, для чего мы и проведем небольшой рефакторинг.</p><h2>Что мы будем рефакторить?</h2><p>Давайте посмотрим на структуру кода.</p><p>Наш проект состоит из 4 классов: главного класса, класса клиента, класса аренды и класса фильма.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/3.png" alt="" /></figure><p>В главном классе мы создаем фильмы и записи об их аренде, вызываем метод вычисления счета и выводим результат на экран.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/4.png" alt="" /></figure><p>В классе клиента есть только метод вычисления счета — это, по сути, единственная логика во всей программе. Ее-то мы и будем рефакторить.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/5.png" alt="" /></figure><p>Для вычисления счета этот класс оперирует данными аренды и фильма. Это, по сути, классы данных, не содержащие в себе никакой логики: аренда содержит в себе ссылку на фильм и срок аренды, фильм содержит в себе название и тип фильма: обычный, детский или новый релиз.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/6.png" alt="" /></figure><figure><img src="https://media.tproger.ru/uploads/2022/10/7.png" alt="" /></figure><p>Давайте представим, что нам надо добавить две новые фичи. Во-первых, клиент хочет вывод еще и в HTML. Во-вторых, клиент хочет добавить другие типы фильмов — например драмы, комедии, триллеры. В текущую структуру эти правки ложатся с трудом, поэтому нам придется ее изменить.</p><h2>Длинные и короткие методы</h2><p>Первый код-смелл, который тут же бросается в глаза — длинный метод. Нередко приходится слышать, что длинные методы — это удобно, ведь все на виду, а при чтении кода, состоящего из множества мелких методов, кажется, что никакого вычисления не происходит вовсе — весь код превращается в цепочку делегирований.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/8.png" alt="" /></figure><p>Так в чем же тогда проблема длинных методов? Основных проблем две:</p><ol><li>Обычно весьма трудно разобраться, что в них происходит.</li><li>Если метод уже стал большим по какой-то причине, то, скорее всего, по этой же причине он и будет расти дальше. Уверен, многие из вас видели «монстров» по несколько сотен и даже тысяч строк.</li></ol><p>В то же время короткие методы имеют серьезные преимущества перед длинными:</p><p>— Понятный код. Если вы хорошо называете методы, то вашему коду не нужны никакие комментарии — вместо них будут работать имена.</p><p>— Простая навигация. Хорошие названия работают как оглавление. Вам не надо читать всю книгу, чтобы понять, в каком месте ее открыть.</p><p>— В конце концов, короткие методы больше способствуют переиспользованию кода.</p><p>Ну хорошо, а как понять, что метод длинный?</p><p>— Если встал вопрос, значит, скорее всего, длинный.</p><p>— Если вопрос не встал — метод, скорее всего, тоже длинный. Ведь в реальных кодовых базах есть очевидное преобладание длинных методов над короткими.</p><p>— Если за несколько секунд не удалось понять, что происходит в методе — он длинный.</p><p>— Наконец, если в нем больше 10-12 строк — абсолютно точно длинный. Хорошая длина метода — от одной до 3-4 строк.</p><p>Что же делать с длинным методом? В 99% случаев — извлекать из него маленькие методы. Извлечение метода должно стать первым инструментом на пути к более чистому коду. Первое, что делает любой программист, научившийся настоящему рефакторингу — начинает безудержно извлекать методы по поводу и без. И именно тогда он и понимает все преимущества коротких методов.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/9.png" alt="" /></figure><p>Что извлекать? Подсказками будут циклы, условные операторы и комментарии. Также стоит смотреть на места с высокой плотностью обращения к одной и той же переменной.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/10.png" alt="" /></figure><p>После каждого рефакторинга я обязательно проверяю, что тесты все еще зеленые, и сохраняю прогресс в системе контроля версий. Это позволит откатиться обратно в случае красных тестов.</p><h2>Feature Envy</h2><p>Следующий код-смелл Feature Envy в русском переводе называется «завистливая функция». Весь смысл объектов в том, данные живут вместе с поведением, однако часто приходится видеть, как метод заинтересован больше в чужом классе, чем в своем. Вот и в нашем случае метод getCharge активно использует данные класса Аренды, а вот методы и поля же собственного класса он попросту игнорирует.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/11.png" alt="" /></figure><p>К счастью, решение очевидно: если ваш волк смотрит в лес — отпустите его туда. То есть, если метод getCharge так интересуется с классом аренды, то, собственно, там ему и место. Давайте перенесем уже, наконец, этот метод в его новый дом. Для этого воспользуемся автоматическим рефакторингом.</p><h2>Локальные переменные</h2><p>Следующее проблемное место в коде — это изобилие локальных переменных. Это не то чтобы код-смелл, скорее просто верный спутник длинных методов. Прочесть локальную переменную можно только в области их видимости. Это и есть причина роста этой самой области и, как следствие, появления длинных методов.</p><p>Другая беда в том, что переменная может не раз изменить значение в течение своей жизни, тем самым неприятно удивив разработчика. Это ее свойство — неиссякаемый источник багов, которые потом может быть сложно отладить.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/12.png" alt="" /></figure><p>Чтобы избавиться от локальной переменной, мы воспользуемся методом рефакторинга «Встраивание переменной».</p><figure><img src="https://media.tproger.ru/uploads/2022/10/13.png" alt="" /></figure><p>Кто-то может возразить, что мы таким образом снижаем производительность нашей системы. Действительно, вместо одного раза метод вызывается дважды — так что озабоченность понятна, но не рациональна.</p><p>О производительности можно и нужно разговаривать. Но, во-первых, только тогда, когда это действительно требуется, во-вторых, исключительно имея реальные замеры кода. То есть оптимизация — это отдельная работа, направленная на устранение бутылочных горлышек. В реальной практике я отказываю себе во встраивании переменной только в случае работы с файловой системой, сетью, ну или если у метода есть сайд-эффект.</p><h2>Продолжаем декомпозировать длинный метод</h2><figure><img src="https://media.tproger.ru/uploads/2022/10/14.png" alt="" /></figure><p>Следующий на очереди к извлечению у нас кусок кода, рассчитывающий очки лояльности, так называемые рентер-поинты.</p><h2>Разделение цикла</h2><figure><img src="https://media.tproger.ru/uploads/2022/10/15.png" alt="" /></figure><p>В этот раз мы воспользуемся методом рефакторинга под названием «Разделение цикла». Он нужен для того, чтобы декомпозировать один цикл на несколько более маленьких циклов.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/16.png" alt="" /></figure><p>Это снова может вызвать озабоченность по поводу производительности. Ответ будет тот же самый: без замеров и реальной необходимости в оптимизации, рассуждать о производительности бессмысленно. Оптимизация — это отдельная от рефакторинга задача, которую заметно легче решать, если ваш код хорошо структурирован.</p><p>Как только мы разделили цикл на несколько, мы можем извлечь цикл в новый метод.</p><h2>Замена цикла конвейером</h2><figure><img src="https://media.tproger.ru/uploads/2022/10/17.png" alt="" /></figure><p>Следующий рефакторинг — замена цикла конвейером, он просто приводит ваш цикл к функциональному стилю. С одной стороны, это чистая вкусовщина. С другой стороны, замена цикла конвейером позволяет вам избавиться от локальных переменных и держать ваши циклы как можно более короткими. Впрочем, должен признать, что содержимое подобных конвейеров иногда бывает трудно прочесть.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/18.png" alt="" /></figure><p>На этом этапе уже можно встать на паузу и посмотреть на получившийся код. У нас есть метод, собирающий текст итогового счета, и есть отдельные методы, которые считают нужные нам данные. Этого нам УЖЕ достаточно, чтобы добавить новую фичу вывода данных в HTML. По сути, все, что осталось в этом коде — заменить текстовые строки на HTML-теги.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/19.png" alt="" /></figure><h2>Switch case</h2><p>Приступим к внедрению второй фичи: нам нужно добавить еще несколько типов фильмов. Мы могли бы поступить следующим образом: добавить новый код, например «драма», пойти в класс аренды и в switch-case добавить отдельное условие. И, если потребуется, добавить еще одно условие в метод расчета очков лояльности.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/20.png" alt="" /></figure><p>Здесь на нашем пути встает еще один код-смелл: switch-case. Это канонический пример нарушения Open-Closed принципа из всем известных SOLID-принципов.</p><p>Главная проблема свитч-кейсов кроется в дублировании: часто мы находим один и тот же свитч-кейс, разбросанный по всему коду, и при добавлении нового условия необходимо потом мучительно искать все эти свитчи. Другая проблема кроется в том, что при каждом новом условии нам придется расширять этот свитч и добавлять в него логику. Думаю, многим доводилось видеть подобные свитчи, разросшиеся на целые сотни строк.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/21.png" alt="" /></figure><p>Решением этой проблемы становится полиморфизм. В данном случае мы можем создать нескольких наследников для класса «фильм», перегрузив метод расчета чека. То есть, это будут классы Regular Movie, Children’s Movie и NewReleaseMovie</p><p>Но есть одна проблема. Мартин Фаулер, автор книги и нашего примера, пишет, что схема с полиморфным фильмом всем хороша, но не всегда работает. По его словам, фильм в течение своей жизни может менять свой тип: новый релиз может перестать быть таковым и просто стать детским. Взамен Мартин предлагает воспользоваться шаблоном проектирования State Pattern: в класс Movie будет добавлено поле Price с тремя наследниками: RegularPrice, ChildrensPrice и NewReleasePrice.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/22.png" alt="" /></figure><figure><img src="https://media.tproger.ru/uploads/2022/10/23.png" alt="" /></figure><p>Теперь добавление нового типа фильма превратилось в банальное создание нового класса с перегрузкой одного или двух методов.</p><h2>«Что дальше?»</h2><p>Давайте посмотрим на первоисточник.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/24.png" alt="" /></figure><p>Единственным способом добавить в него HTML-вывод было бы просто скопировать тело метода и заменить текст на теги. Но в таком случае при каждом изменении правил ценообразования или при добавлении нового жанра нам пришлось бы править все в двух местах. А добавляя новые жанры в этот метод, мы бы сделали его огромным и неподдерживаемым источником багов. Теперь этот код выглядит так:</p><figure><img src="https://media.tproger.ru/uploads/2022/10/25.png" alt="" /></figure><p>Самое примечательное, что в процессе рефакторинга мы не придумывали хитрые решения, не разрабатывали сложные схемы, чтобы добавить новые фичи. Все, что мы делали, — просто устраняли код-смеллы. И в результате каждая новая фича сама собой ложилась в структуру кода. Именно эта способность хорошо отрефакторенного кода принимать новые фичи — главное преимущество рефакторинга. Потратив немного времени на рефакторинг сейчас, вы втрое сэкономите себе время потом, когда вам понадобится новая фича.</p><p>Перечисленных здесь код-смеллов и методов рефакторинга хватает для большинства задач.</p><h2>Тесты и требования к ним</h2><p>Однако этих знаний недостаточно, чтобы можно было сразу браться за рефакторинг реальной кодовой базы. Во-первых, для такого рефакторинга нужны весьма конкретного вида тесты. Во-вторых, необходимо понять, как встроить рефакторинг в свой рабочий процесс — то есть ответить на вопросы: когда его делать и как договориться об этом с другими.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/26.png" alt="" /></figure><p>Начнем с тестов. Первая проблема современных кодовых баз — уровень покрытия. В командах обычно принято какое-то правило, например «80% покрытия тестами», что сразу вызывает вопрос: неужели это нормально, что 20% программы не работает?</p><p>Конечно, нет. Рефакторинг требует уверенности.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/27.png" alt="" /></figure><p>Самый лучший способ добиться полного покрытия — просто не писать код, на который не написан тест. Это может звучать невероятно глупо, но, поверьте, это работает, и работает очень круто. Этот подход, описанный Кентом Беком, называется Test-Driven Development.</p><p>Второе требование к тестам — тесты не должны быть хрупкими!</p><figure><img src="https://media.tproger.ru/uploads/2022/10/28-autoconverted.jpeg" alt="" /></figure><p>Помню, как я смотрел свой первый курс лекций по юнит-тестам. Именно там я узнал, что в словосочетании юнит-тест «юнит» — это класс. Я начал писать классы парами: продакшн класс и тестовый класс. Все взаимодействия с другими классами в такой ситуации необходимо было закрывать моками. Потом, прочитав книжку по рефакторингу, я начал активно рефакторить все подряд. И какова же была моя боль, когда при каждом переносе метода или извлечении класса мне приходилось мучительно переписывать тесты.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/29.png" alt="" /></figure><p>Так я впервые столкнулся с проблемой «хрупких тестов». Тесты не помогали рефакторить, как предполагалось. Наоборот, они невероятно сильно мешались под ногами. Это очень глупая ситуация, ведь тесты только для того и нужны, чтобы быть уверенным, что поведение написанного кода не изменилось при редактировании. А это и есть определение рефакторинга — изменение кода без изменения поведения. В итоге я попал в ситуацию, когда тесты свою задачу практически не выполняли.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/30.jpg" alt="" /></figure><p>Через некоторое время я узнал, в чем была проблема. Уже упомянутые Мартин Фаулер и Кент Бек, как оказалось, пишут тесты совсем иначе. В словосочетании «юнит-тест» для них слово «юнит» означало не «класс» и не «метод», а «поведение» — наименьший неделимый фрагмент функциональности.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/31.png" alt="" /></figure><p>Они не глушили тестируемый класс со всех сторон: наоборот, такие юнит-тесты проверяли целые связки классов. Это не значит, что они не использовали заглушки и моки, просто заглушки использовались только для замены внешних слоев приложения, не относящихся к бизнес-логике, то есть глушились классы взаимодействия с базами, очередями, вебом и иными внешними системами, а также, например, пользовательский интерфейс — другими словами, слои ввода и вывода данных.</p><p>Покажу на примере. Возьмем типичное корпоративное приложение со множеством интерфейсов ввода и вывода. В центре такого приложения всегда лежит бизнес-логика. Код, связывающий бизнес-логику со внешними системами, упакован в отдельные слои — gateway. Это могут быть репозитории, DAO, или, например, сложный клиентский код внешней системы.</p><figure><img src="https://media.tproger.ru/uploads/2022/10/32.png" alt="" /></figure><p>Все, что необходимо глушить для того, чтобы тесты не мешали рефакторингу — это gateway. А чтобы код самих gateway тоже можно было рефакторить, пишутся отдельные тесты — интеграционные.</p><h2>Как встроить рефакторинг в свой рабочий процесс?</h2><p>Обычно самый первый вопрос — как правильно выделить время под рефакторинг. Заводить ли для этого отдельные таски и когда рефакторить, если фичи в приоритете?</p><p>Ответ такой: рефакторинг должен проводиться непрерывно на всем этапе разработки.</p><p>Обычно это выглядит так:</p><p>— Вы взяли задачу и начали читать код. На этом моменте обычно вам уже придется что-то подрефакторить.</p><p>— Потом вы приступили к разработке. Буквально через 5-10 минут, максимум полчаса-час, у вас должен быть рабочий кусок кода, покрытый тестом. Тут же надо его рефакторить.</p><p>— Снова разработка, снова покрытый тестами кусок кода через непродолжительный отрезок времени.</p><p>И такими вот циклами и движетесь вперед до решения задачи. Как только вы наткнулись на кусок кода, в который ваша фича не лезет никак — останавливаетесь в прогрессе и начинаете рефакторить.</p><p>Если вы делаете это в парадигме TDD, то этот подход будет называться Red-Green-Refactor. То есть сначала пишете красный тест на какой-то фрагмент функционала, потом пишете код, который делает ваш тест зеленым, потом рефакторите.</p><p>Отсюда и следуют ответы на другие вопросы:</p><p>— Заводить ли под рефакторинг отдельные таски?</p><p>— Нежелательно, запланированный рефакторинг должен быть вынужденной редкостью, а не постоянной практикой.</p><p>— Когда рефакторить, если надо делать фичи?</p><p>— Плохой код делает вас медленнее. Поэтому рефакторинг — это один из главных способов ускорить доставку новых фич. Но в плохой кодовой базе очень легко погрязнуть в рефакторинге надолго, поэтому и было придумано правило бойскаута: просто оставляйте код в лучшем состоянии, чем он был до вас. И рано или поздно вы обнаружите себя во вполне чистой кодовой базе.</p><h2>Как договориться с коллегами?</h2><p>Может оказаться, что менеджеру не понравится, что вы ковыряетесь в старом коде вместо того, чтобы делать новый. А может быть, и коллегам не понравится, что вы трогаете старый код с риском сломать его — особенно если это их код.</p><p>Что ж, самое простое тут — это менеджер. Рефакторинг — способ двигаться быстрее, так что можно попробовать зайти с экономической стороны вопроса. Но, в целом, можете просто ничего про рефакторинг не рассказывать, не его это дело. Фича готова? Готова. А как именно — это уже детали реализации. Самое главное — помнить, что вы профессионал и вам лучше видно, как делать свою работу.</p><p>Если против вас ополчилась команда и договориться не удалось никак, то тут все плохо. Обычно такие глубокие разногласия приводят к разводам — кто-то уходит, а кто-то остается. Вопрос лишь в том, кто у руля.</p><p>На этом мы, пожалуй, и закончим знакомство с НАСТОЯЩИМ рефакторингом.</p><p>И теперь у вас уже достаточно инструментов, чтобы безопасно и качественно приводить свою кодовую базу в порядок.</p>]]></content:encoded>
    </item>
    <item>
      <title>10 лайфхаков для Android-разработчика: полезные extensions на Kotlin</title>
      <link>https://tproger.ru/translations/10-lajfhakov-dlja-android-razrabotchika-poleznye-extensions-na-kotlin</link>
      <comments>https://tproger.ru/translations/10-lajfhakov-dlja-android-razrabotchika-poleznye-extensions-na-kotlin?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Борисенков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/10-lajfhakov-dlja-android-razrabotchika-poleznye-extensions-na-kotlin</guid>
      <description><![CDATA[<p>Советы и extensions, которые делают код на Kotlin чище: например, привычка всегда добиваться зелёной галочки и общий профиль проверок в репозитории.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/10-lajfhakov-dlja-android-razrabotchika-poleznye-extensions-na-kotlin">10 лайфхаков для Android-разработчика: полезные extensions на Kotlin</a>»</p>]]></description>
      <category><![CDATA[Android]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 06 Apr 2021 11:22:18 GMT</pubDate>
      <content:encoded><![CDATA[<p>Вот несколько советов и extensions на Kotlin, которые будут полезны в ежедневной работе Android-разработчика.</p><h2>Совет 1: Добивайтесь зелёной галочки</h2><p>Android Studio предоставляет множество встроенных <a href="https://www.jetbrains.com/help/idea/code-inspection.html">проверок</a>: от юнит-тестов до линтёра. К сожалению, пока нет хорошего способа убедиться, что на CI нет предупреждений, поэтому мы разместили <a href="https://gist.github.com/gpeal/97177ad27f38ef006495b0690415eba9">наш профиль для проверки</a> в репозитории, чтобы все видели один и тот же набор предупреждений.</p><p>Затем мы взяли за правило ВСЕГДА избавляться от предупреждений (фиксить или заглушать их). При появлении новых предупреждений (из-за рефакторинга или добавления новых проверок), мы следуем <a href="https://martinfowler.com/bliki/OpportunisticRefactoring.html">правилу бойскаута</a> (всегда оставляй после себя код в лучшем состоянии, чем до тебя).</p><figure><img src="https://media.tproger.ru/uploads/2021/04/greenCheckmark.png" alt="" /></figure><p>Однако есть более сложный случай — точки входа, такие как фрагменты, на которые ссылаются только с помощью рефлексии (Android Studio помечает их как неиспользуемые). Чтобы избавиться от таких предупреждений мы сделали <a href="https://gist.github.com/gpeal/4bddd5c5f52765fb47df234d4a4b2ba8">TonalEntryPoint</a>.</p><p>Используя эту аннотацию в классе или конструкторе, который помечен как неиспользуемый, и нажимая alt+enter, мы получим возможность добавить неиспользуемый код в список точек входа.</p><h2>Совет 2: Относитесь к предупреждениям Kotlin, как к ошибкам</h2><p>Для этого добавим следующий код в файл build.gradle:</p><h2>Совет 3: Делегат ViewBinding</h2><p>Мы очень любим <a href="https://developer.android.com/topic/libraries/view-binding">View Binding</a>. Он отлично работает, и мы полностью перешли на него с ButterKnife/Kotlin Synthetics. Однако, синтаксис его создания не идеален, поэтому мы создали собственный делегат, чтобы создавать его в соответствии с жизненным циклом, это выглядит следующим образом:</p><p>Здесь можно посмотреть <a href="https://gist.github.com/gpeal/9925b6333220dcdd3ad29d7c5081c5ea">полный код</a>.</p><h2>Совет 4: fadeTo(visible)</h2><p>Необходимость скрыть/показать view возникает часто. Для улучшения пользовательского опыта мы создали функцию fadeTo() в качестве замены для View.setVisibility() или View.isVisible = true/false.</p><p>Код функции можно найти на <a href="https://gist.github.com/gpeal/2784b455cfd22d7ba567fa9c24144656">GitHub</a>.</p><h2>Совет 5: mapDistinct()</h2><p>Это простое сокращение для Flow.map().distinctUntilChanged().</p><h2>Совет 6: uniqueObservable()</h2><p><a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.properties/-delegates/observable.html">Delegates.observable</a> в Kotlin очень полезен. Однако:</p><ol><li>Иногда нам нужно делать что-то только при изменении значения.</li><li>В таком случае писать лямбда-выражение с тремя параметрами и проверку на равенство чересчур шаблонно.</li></ol><p>Поэтому мы написали <a href="https://gist.github.com/gpeal/7505f6308e20a0dd59c2fd2141e19159">uniqueObservable()</a>, который будет вызывать лямбду только если значение изменится.</p><h2>Совет 7: Отладка + Корутины и Broadcast Receivers</h2><p>В настоящее время Broadcast Receivers (широковещательные приёмники) редко используются в Android. Однако они могут быть крайне полезны для тестов в процессе отладки.</p><p>Мы используем debug-only широковещательные приемники, которые, по сути, дают нам CLI для установки/обновления параметров в коде без необходимости вызывать меню отладки в UI или пересобирать приложение.</p><p>Наш coroutine friendly код будет выглядеть так:</p><h2>Совет 8: ConflatedJob</h2><p>Корутины часто используются для отмены выполнения Job, перед запуском нового (например когда пользователь делает pull to refresh). <a href="https://gist.github.com/gpeal/b77509b46bd6d1db557b952731d01101">ConflatedJob</a> автоматически отменяет старый Job, перед запуском нового.</p><h2>Совет 9: Timber Property Delegate</h2><p>Для логгирования мы используем Timber, однако его синтаксис Timber.tag(TAG).i(…)  — слишком громоздкий.</p><p>По аналогии с View Binding Delegate, мы написали делегат, который упрощает вызов:</p><p>Он автоматически создает TAG на каждое свойство (а не на строку журнала, как <a href="http://jakewharton.github.io/timber/timber/log/Timber.DebugTree.html">Timber.DebugTree</a>), сокращает общие суффиксы, такие как “Impl” и “ViewModel” и обрезает теги до 23 символов.</p><p>Полный код на <a href="https://gist.github.com/gpeal/2666c668c9e02e3064e87bcd0557e070">GitHub</a>.</p><h2>Совет 10: Number.dp</h2><p>Так как в макетах обычно используют dp, а в свойствах View пиксели, часто в коде требуется конвертировать dp в пиксели. Именно поэтому <a href="https://stackoverflow.com/questions/4605527/converting-pixels-to-dp?answertab=votes#tab-top">вопрос о том как это сделать</a> пользуется популярностью на StackOverflow. Мы используем для этого следующий extension на Kotlin:</p><p>С помощью этой функции очень просто обновить padding:</p>]]></content:encoded>
    </item>
    <item>
      <title>Как влюбить в себя своего код-ревьюера: правила подготовки к code review</title>
      <link>https://tproger.ru/translations/kak-vljubit-v-sebja-revjuera</link>
      <comments>https://tproger.ru/translations/kak-vljubit-v-sebja-revjuera?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Олег Борисенков]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/kak-vljubit-v-sebja-revjuera</guid>
      <description><![CDATA[<p>Рассказываем про техники подготовки к code review, которые помогут получить от него максимальную пользу и порадуют ревьюера.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/kak-vljubit-v-sebja-revjuera">Как влюбить в себя своего код-ревьюера: правила подготовки к code review</a>»</p>]]></description>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Кодстайл]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Thu, 03 Dec 2020 12:32:05 GMT</pubDate>
      <content:encoded><![CDATA[<p>Под подготовкой к code review обычно понимают подготовку самого код-ревьюера. Но вклад разработчика тоже важен. Однако если вы постараетесь следовать всем советам из этой статьи, ваш ревьюер может и не влюбится в вас, но будет доволен.</p><h2>Как улучшить code review?</h2><ul><li>Учитесь быстрее — правильно составляйте список изменений в коде, это поможет ревьюеру сконцетрироваться на главном и дать полезный фидбек.</li><li>Делайте других лучше — подавайте пример коллегам.</li><li>Избавьтесь от конфликтов в команде — code review это всегда споры. Относитесь к ним обдуманно и последовательно.</li></ul><p>Золотое правило: цените чужое время.</p><p>Старайтесь сами находить ошибки, это полезно для вас обоих. Относитесь к ревьюеру не как к препятствию, а как к достойному доверия союзнику.</p><h2>Техники подготовки к код ревью</h2><h3>Сделайте первое ревью самостоятельно</h3><p>Дайте коду полежать до утра, а потом попробуйте представить, что видите его в первый раз. Что в нём вас смущает? Посмотрите на изменения в том же виде, в котором их увидит ревьюер. Избегайте повторяющихся ошибок (лишний файл, остатки отладочного кода).</p><h3>Напишите понятный список изменений</h3><p>Хороший changelist объясняет, что изменилось на верхнем уровне и почему сделано это изменение. Помните, что читатель может не обладать теми же знаниями о проекте, что и вы.</p><p>Подробнее про списки изменений в статьях <a href="https://chris.beams.io/posts/git-commit/">«How to Write a Git Commit Message»</a> и <a href="https://dhwthompson.com/2019/my-favourite-git-commit">«My favourite Git commit»</a>.</p><h3>Автоматизируйте рутину</h3><p>Не тратьте время ревьюера на проверку отступов у скобок. Отправляйте код на ревью только после того, как все <a href="https://mtlynch.io/human-code-reviews-1/#let-computers-do-the-boring-parts">автотесты отработали без ошибок</a>. Используйте <a href="https://www.atlassian.com/git/tutorials/git-hooks">Git Hooks</a> для проверки кода перед отправкой.</p><h3>Пусть ваш код говорит сам за себя</h3><p>Вы можете объяснить одному человеку как работает ваш код, но лучше, чтобы код был понятен каждому. Поэтому при подготовке к код ревью попробуйте посмотреть на свой код глазами не знакомого с ним человека.</p><p>Помните, что в сложных случаях можно использовать комментарии.</p><h3>Делайте узконаправленные изменения</h3><p>Лучшая тактика — <a href="https://blog.codinghorror.com/curlys-law-do-one-thing/">одно изменение для одной проблемы</a>. В противном случае ревьюеру будет сложно понять, какие изменения служат цели А, а какие цели Б.</p><h3>Разделяйте функциональные изменения и рефакторинг</h3><p>Пара строк функциональных изменений затеряется при рефакторинге. Чтобы этого избежать, действуйте в следующем порядке:</p><ol><li>Добавьте поведенческие тесты.</li><li>Проводите рефакторинг, не изменяя код тестов.</li><li>Измените логику и обновите тесты.</li></ol><h3>Разделяйте длинные changelist’ы</h3><p>Если при подготовке к code review вы видите, что список изменений слишком разросся, подумайте о том, что нужно добавить сейчас, а с чем можно повременить.</p><h3>Адекватно воспринимайте критику</h3><p>При подготовке к code review морально настройтесь, что скорее всего вы получите в том числе негативный отклик. Лучший способ угробить code review — принять критику слишком близко к сердцу. Это особенно легко сделать, когда ревьюер формулирует фидбек <a href="https://mtlynch.io/human-code-reviews-1/#never-say-you">как личные претензии</a>.</p><p>Тем не менее, как автор вы полностью <a href="https://mtlynch.io/book-reports/7-habits-of-highly-effective-people/#habit-1-be-proactive">контролируете свою реакцию на ревью</a>. Конечно, иногда первое желание — попытаться оправдаться. Но лучше просто поблагодарить ревьюера за внимательность.</p><h3>Будьте терпеливы к ошибкам</h3><p>Иногда ревьюеры откровенно ошибаются, но это может быть индикатором того, что нужны изменения. Например, это могут быть какие-то неявные языковые особенности, которые не понятны неспециалисту.</p><h3>Отвечайте на code review</h3><p>Установите в вашей команде правила, которые помогут определить, кто сейчас работает над кодом (вы или ревьюер). Это поможет не мешать друг другу в процессе. Отвечайте на каждое замечание настолько развернуто, насколько это требуется.</p><h3>Задавайте вопросы правильно</h3><p>Если попадается непонятное замечание, спросите: «Что будет полезно изменить?». Также можно попробовать угадать намерения ревьюера и предварительно отредактировать код — это может помочь, даже если вы не совсем попали в цель.</p><h3>В неоднозначных ситуациях отдайте преимущество ревьюеру</h3><p>Некоторые вещи в коде — дело вкуса и их сложно рационально аргументировать. Однако если ревьюер что-то предлагает, подумайте, возможно, его свежий взгляд объективнее.</p><h3>Оперативно отвечайте на комментарии</h3><p>Чем дольше вы отвечаете на замечания, тем больше времени потребуется для того, чтобы вспомнить контекст (и вам, и ревьюеру). После отправки кода на code review, сделайте главным приоритетом ответ на него.</p><p>Расскажите, а как проходит ваша подготовка к код ревью? Готовитесь ли вы как-то вообще и считаете ли это необходимым?</p>]]></content:encoded>
    </item>
    <item>
      <title>Стоит прочитать: обзор книги Роберта Мартина «Чистый код. Создание, анализ и рефакторинг»</title>
      <link>https://tproger.ru/books/clean-code-review</link>
      <comments>https://tproger.ru/books/clean-code-review?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/books/clean-code-review</guid>
      <description><![CDATA[<p>Книга Роберта Мартина «Чистый код. Создание, анализ и рефакторинг» состоит из трёх частей и учит начинающих программистов писать правильный код.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/books/clean-code-review">Стоит прочитать: обзор книги Роберта Мартина «Чистый код. Создание, анализ и рефакторинг»</a>»</p>]]></description>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Стоит прочитать]]></category>
      <category><![CDATA[Книги]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Tue, 17 Nov 2020 06:39:00 GMT</pubDate>
      <content:encoded><![CDATA[<p>Одно из самых ярких впечатлений на меня произвела книга консультанта и автора в области разработки ПО Роберта Мартина «Чистый код. Создание, анализ и рефакторинг», написанная в 2012 году. Начиная работу, автор пишет:</p><blockquote>Даже плохой программный код может работать. Однако если код не является «чистым», это всегда будет мешать развитию проекта и компании-разработчика, отнимая значительные ресурсы на его поддержку и «укрощение».</blockquote><p>В своей книге специалист подробно рассказывает о том, как избежать ошибок при написании кода и сделать его чистым.</p><p>Книга включает в себя три части. В первой автор описывает основные принципы и приемы создания чистого кода и приводит примеры такого «правильного» кода. Во второй части он демонстрирует сценарии, представляющие собой упражнения по чистке кода или по преобразованию плохого кода в код с меньшим числом ошибок. Последняя часть представляет собой главу, в которой автор описывает путь мышления человека в процессе чтения, написания и чистки кода.</p><p>На мой взгляд, книга написана простым языком, поэтому освоить ее сможет даже начинающий программист. Я бы порекомендовала ее людям, только начинающим осваивать профессию, поскольку важно усвоить принципы написания правильного кода в самом начале работы.</p><h2>Общие правила написания чистого кода</h2><p>В начале повествования автор сосредотачивает внимание на основных постулатах создания правильного кода. Прочитав эту часть, я поняла, что до того, как код отдан тестировщикам, важно продумать, какие могут возникнуть проблемы и ошибки. Необходимо понять, какой кейс не был учтен. Стоит полностью продумать свою задачу и только после этого приступать к ее выполнению. Ведь если человек начинает писать код, не продумав все заранее, он может столкнуться с ошибками, ему придется возвращаться назад, то есть выполнять двойную работу. Мне кажется, что это очень важный совет. Применяя его, сейчас я экономлю очень много времени и энергии.</p><p>Автор сравнивает написание кода с созданием книги. В обоих случаях сначала пишется черновик, в котором заранее продумываются все ходы, и только потом создается чистовик, содержащий лучший вариант. Если специалист хочет стать хорошим программистом, ему всегда нужно продумывать заранее ход работы.</p><p>Кроме того, не нужно бояться что-то менять в уже созданном коде. Часто программисты работают с чужим кодом и не меняют какие-то его элементы, поскольку бояться столкнуться с непредсказуемыми ошибками. По мнению автора, такая стратегия ошибочна, потому что она обязательно приведет к еще большему количеству ошибок. Также одним из основных постулатов создания чистого кода является принцип единой ответственности. Согласно ему, любая сущность должна выполнять только одно действие и нести только одну ответственность.</p><p>Автор говорит также о том, что код должен читаться сверху вниз. Функции лучше располагать так, чтобы при чтении одной, сразу было видно другую. Если же блоки кода находятся в разных местах, это очень затрудняет чтение.</p><p>Наконец, Р. Мартин отмечает важность удаления мертвого кода. Это код, который никогда не сработает или тот код, который уже не используется. По мнению автора, такие коды не приносят никакой пользы и удалять их нужно сразу же, чтобы все было структурировано. Далее я хотела бы рассказать о тезисах книги, которые кажутся мне наиболее важными.</p><h2>Наименование</h2><p>При написании кода очень важно задавать верное название, поскольку наименование обеспечивает читаемость кода. Необходимо использовать наиболее понятные и подходящие наименования. Прочитав название метода или сущности, специалист должен сразу понять, за что она ответственна. Стоит также помнить о принципе единой ответственности, и если программист ему следует, то функция вряд ли будет содержать вставки “And” или “With”, поскольку функция выполняется лишь одно действие. При описании функции важно всегда использовать глаголы. Также желательно, чтобы в названии было ключевое слово, которое поможет понять, о чем эта функция.</p><p>Важно избегать одинаковых наименований. Если в вашем коде имеются функции с одинаковыми названиями, вероятно, они выполняют одно и тоже же действие. Если специалист назвал функции одинаково, а они выполняют разные действия, их нужно переименовать так, чтобы было понятно, что конкретно они делают. Если они выполняют одинаковые действия, но, например, с изменением одного параметра, то важно продумать, как сделать так, чтобы функция не дублировалась. Иными словами, если у двух функций одинаковое наименование, скорее всего, нужно что-то менять.</p><p>Наименования лучше не сокращать, а прописывать полностью. Например, если используется слово «product», название «pr» будет не очень понятным для читателя. Важно, чтобы название полностью отражало ответственность сущности.</p><h2>Функции</h2><p>По мнению Р. Мартина, функции должны быть максимально короткими. Их длина не должна быть больше двадцати строк, а сами строки не должны быть длиннее ста пятидесяти символов. В противном случае ее лучше делить на части. Кроме того, важно, чтобы функция выполняла только одну операцию. Что касается входных параметров, в идеале их не должно быть. Но если входные параметры требуются, то их должно быть не больше трех. Чем больше входных параметров, тем тяжелее читать функцию.</p><h2>Комментарии</h2><p>Слишком большое количество комментариев к коду означает, что написан он плохо. Комментировать код стоит только тогда, когда он может быть непонятен другому разработчику или самому создателю этого когда через какое-то время. Также не нужно комментировать плохой код, его необходимо просто переписать. Например, если специалист начинает работу над проектом другого программиста, и он видит ошибки, эти ошибки нужно сразу же исправлять.</p><p>Недописанный или некорректно работающий код всегда нужно комментировать, используя соответственно одно из двух ключевых слов «TODO» или «FIXME». В таком случае программист будет сразу понимать, что код не готов и его нужно дописывать. Также часто разработчики комментируют функции или части кода, чтобы когда-то к ним вернуться, и это переходит из ветки в ветку. Такие комментарии лучше не оставлять. Системы контроля версий хранят все состояния кода, поэтому необходимости переносить закомментированный код из ветки в ветку нет.</p><h2>Форматирование кода, классы и обработка ошибок</h2><p>При форматировании кода необходимо придерживаться общей стилистики проекта, его общепринятой архитектуры. Например, если в проекте используется архитектура V.I.P.E.R., то нужно использовать ее, иначе можно испортить код. Если же написание проекта начинается с нуля, то необходимо заранее продумать, какую архитектуру использовать и какой стилистики придерживаться.</p><p>Классы обязательно должны быть компактными. Название класса должно отображать его ответственность. И к ним также применяется принцип единой ответственности. Кроме того, Р. Мартин говорит о том, что по отношению к классам, как и по отношению к функциям, важно применять принцип компактности. Не стоит создавать большие классы, лучше разбивать их на подклассы. Важно создавать все новые подклассы до тех пор, пока не получится максимально раздробленная и понятная структура.</p><p>Наконец, при обработке ошибок всегда важно использовать exсeptions (исключения) вместо возвращения кода ошибок напрямую. Обработка ошибок всегда должна представлять собой только одну операцию. В функции, которая ответственна за обработку ошибки, ничего другого после произведения обработки выполняться не должно. Хотя на это вам намекнет и сам компилятор.</p><p>Книга Р. Мартина является сводом правил по написанию правильного кода, которым каждый программист должен следовать. На мой взгляд, умение писать чистый код – важный навык, помогающий специалисту не только самому понимать свой код лучше, но и работать в команде. Именно поэтому я уверена в том, что, если после этой статьи хоть один разработчик узнает о данной книге и прочтет ее, в мире станет на одного хорошего разработчика больше.</p>]]></content:encoded>
    </item>
    <item>
      <title>Какие привычки программистов мешают писать хороший код и как от них избавиться — отвечают эксперты</title>
      <link>https://tproger.ru/experts/bad-programmers-habits</link>
      <comments>https://tproger.ru/experts/bad-programmers-habits?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Никита Прияцелюк]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/experts/bad-programmers-habits</guid>
      <description><![CDATA[<p>Эксперты рассказывают о вредных привычках программистов: написание велосипедов, ненужный рефакторинг, и как от них избавиться.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/experts/bad-programmers-habits">Какие привычки программистов мешают писать хороший код и как от них избавиться — отвечают эксперты</a>»</p>]]></description>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Ответы экспертов]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Mon, 14 Oct 2019 14:41:25 GMT</pubDate>
      <content:encoded><![CDATA[<p>Костыли, велосипеды, ненужный рефакторинг — список вещей, об которые может споткнуться начинающий (и не очень) программист, можно продолжать долго. О том, какие плохие привычки встречаются у программистов и как с ними бороться, мы решили узнать у наших экспертов.</p><p>Мы видим много новичков в веб-разработке, мы их учим. Многие меняют жизнь кардинально и не обладают никаким опытом, а кто-то приходит к нам, попробовав что-то на других платформах. У большинства можно встретить типовые ошибки и заблуждения, которые ведут их не туда, мы стараемся корректировать поведение студентов и обращаем на это внимание.<br />Когда только начинаешь кодить, у тебя всё получается, ведь задачи перед тобой стоят простые. В этот момент легко очароваться и почувствовать своё всемогущество. Рынок стонет и рассказывает нам, как много неадекватных джунов приходит на собеседования. Это напрямую связано с опасным заблуждением новичков, что они уже классные, если могут что-либо собрать на определённом стеке. Нужно помнить, что профессионала не в последнюю очередь определяет количество ошибок и проблем, с которыми он справился.</p><p>Есть другая сторона медали. Встречаются новички, которые боятся ошибок, а они случаются у всех. Часто ребята опускают руки, понимая, что код постоянно не работает. На самом деле ошибки — это помощники программиста. С ними нужно учиться работать, как и с любыми технологиями. Профессионалы больше проводят времени с нерабочим проектом, чем с рабочим, ведь в этом и есть цель — включить что-то неработающее или исправить проблему.</p><p>Третья группа заблуждений связана с самими технологиями. Для большинства задач уже написаны библиотеки, которые упрощают написание кода. Но это не значит, что надо сфокусироваться на изучении библиотек. Если ты действительно хорошо знаешь JavaScript и добавляешь к этому базу computer science, тебе не важно React, Vue, Angular или новенький Svetle указан в ТЗ. Просто нужно время на чтение документации. Словом, нужно всегда стараться смотреть на более низкий уровень технологий и разбираться, как всё работает «под капотом».</p><p>Есть ещё проблема нежелания расширять кругозор, предпочитая учить что-то одно. Безусловно, на старте нужно привыкнуть к новому синтаксису или особенностям какого-то языка программирования или фреймворка. Но на самом деле учиться надо программировать, а не писать на чем-либо. Программирование — это не про конкретный язык, это про то, как писать инструкции компьютерам. Человечество многогранно изучило этот вопрос ещё до появления JS или Python. Парадигмы, паттерны, структуры данных, алгоритмы — это всё великое наследие, не стоит им пренебрегать.</p><p>Отдельно в этом контексте хочется отметить новичков, которым нравится верстать, но они не могут назвать хотя бы три студии или дизайнера, чьи работы их вдохновляют.</p><p>Напоследок самое, пожалуй, неочевидное. Изучая программирование, необходимо тренировать не только хард скиллы, но и софт скиллы. Умение задавать вопросы на Stack Overflow, проходить собеседование, вести себя в технической команде, — часто про это забывают. При этом, быть приятным человеком со знанием основ часто лучше, чем быть гуру, но мерзавцем.</p><p>Многие новички считают свой код идеальным, но они заблуждаются. Идеального кода не существует! Любой код имеет возможности совершенствоваться. Как сказал один из наших программистов:</p><p>Сколько бы лет ты не занимался написанием кода, новый всё равно будет лучше и лучше предыдущего.</p><p>У начинающих разработчиков, узнавших о новом фреймворке или изучавших новый язык программирования, часто возникает непреодолимое желание прямо здесь и сейчас переписать проект с нуля, «теперь-то уже точно правильно». Поспешим расстроить — это плохая практика. Реальность такова, что за каждым проектом обычно стоят бюджеты и сроки. Остановка разработки на несколько месяцев при переписывании проекта обычно никому не нужна. Постепенное переделывание проекта по модулям — хорошо. Снос до основания и переделывание с нуля — плохо.</p><p>Ещё один из немаловажных вопросов — стоит ли бояться костылей? Нет, но к ним нужно относиться настороженно. Это рабочий, но не универсальный код, который желательно отметить комментарием. Костыли могут теряться со временем и при расширении/увеличении программы костыль скорее всего сломается/придётся от него избавиться. Костыли — это меньшее зло. Большее — сорвать сроки!</p><p>Любому программисту всегда следует читать мануалы. Мануалы — любая литература/статьи/обзоры/видео по той теме которую делаешь. Причем начинать можно с азов: if, переменные, память, — и до чего-то объёмного: паттерны, архитектуры кода и что-то обособленное — VR, вёрстка, оптимизация. Если застряли — спрашивайте у опытных товарищей или на форуме. Можно сначала поискать информацию в интернете, а можно сразу бежать за советом — это вопрос совести.</p><p>Чужой код — это ловушка. Если вы не знаете, как это работает, — значит, не знаете и результат работы кода.</p><p>Плагин = чужой код, который ты не знаешь.</p><p>Будьте всегда готовы, что ОНО обновится и отвалится. Поэтому плагины стоит использовать с осторожностью.</p><p>Для того, чтобы у вас как у начинающего программиста получился хороший код, необходимо придерживаться следующих правил:</p><ul><li>Следуйте стандартным правилам оформления кода.</li><li>Пишите комментарии к своему коду в процессе написания. Что делает каждая из функций, её положительные и отрицательные стороны. Также стоит помнить, что лучший комментарий — это говорящие сами за себя названия функций и переменных.</li><li>Не копируйте чужой код. Вместо этого изучите его. Как он работает и будет ли он полезен вам?</li><li>Избегайте использования аналогичных кусков кода.</li><li>Не забывайте проверять свой код на наличие ошибок.</li><li>Проектируйте код с расчётом на дальнейшее расширение функциональности.</li><li>Не полагайтесь на то, что определённые типы данных (integer, указатели и временные метки) будут иметь конкретную длину (например 32 бита), потому что этот параметр отличается на разных платформах.</li></ul><p>В профессии программиста существуют как хорошие, так и плохие привычки, но всё зависит именно от вас. Как вы распределяете своё время и насколько стараетесь совершенствовать свой код. Помните, что любой код имеет свойство устаревать.</p><h2>Перфекционизм</h2><p>Бывало ли у вас такое, что вы пишете код, потом понимаете, что могли бы сделать его более понятным или производительным, а потом начинаете усердно перерабатывать эти строки кода? Загвоздка заключается в том, что вас вновь не устраивает полученный результат, и вы думаете про себя: «Тут можно лучше назвать переменную, это лучше вынести в функцию», — и далее по списку. В итоге вы тратите много времени, а код становится лишь более понятным. Получается, что вы придерживаетесь «перевёрнутого» правило Парето: тратите 80 % усилий и получаете 20 % результата. Я ни в коем разе не призываю не рефакторить код во время его написания, но нужно держать во уме ту грань времени, после которой вы честно можете себе сказать, что нельзя сейчас всё сделать идеально и что будете двигаться дальше в решении задачи, а по прошествии времени, когда вы вновь столкнётесь с этим кодом, у вас уже может быть другое мнение о том, как организовать этот участок кода, и тогда вы сделаете это быстро.</p><h2>Ранние оптимизации</h2><p>Иногда программист, прочитав статью «трюки и оптимизации», начинает повсеместно использовать найденные решения, не задумываясь о недостатках приёмов. Оптимизация является противоположностью рефакторинга, т. к. оптимизации делают код более простым для понимания компьютера, а рефакторинг — для человека. И каждый раз, когда вы будете заменять деление на два на смещение или хитро храните два значения в одном поле, стоит понимать, что вы сохраните 1 байт памяти или наносекунды процессорного времени, но потеряете в лёгкости чтения кода, что усложнит поддержку компонента.</p><p>Решайте проблему производительности по мере её поступления, т. к., возможно, никакие оптимизации и не потребуются. Дополнительная задержка, которая в итоге получается из-за отсутствия этих оптимизаций, может никак не ощущаться конечным пользователем.</p><h2>«Велосипеды»</h2><p>Тут всё просто: всегда перед решением задачи проверяйте, не решил ли её кто-то другой. Да, написание своего это всегда увлекательно, но то количество времени и багов, которое в итоге вы получите, часто не стоит того.</p><h2>Пренебрежение трендами</h2><p>ИТ — это такая область, где что-то новое появляется и меняется достаточно часто. Это могу быть новые подходы к решению задач, фреймворки, алгоритмы и многое другое, что может ускорить и упростить работу.</p><p>Чтобы избежать закостенелости, не обязательно каждый день что-то читать про IT. Достаточно раз в полгода/год ходить на конференции или митапы, чтобы послушать, как сейчас решают различные задачи в других компаниях или же почитывать тематические блоги.</p><h2>«Одинокий рейнджер»</h2><p>Наверняка вы сталкивались или столкнётесь с человеком, который всю работу хочет делать сам и более никому не доверяет. Вся проблема заключается в том, что один человек не может «закрывать» все задачи проекта, да и пропускная способность выполнения проекта будет сильно ограничена одним человеком. Плюс ко всему такие люди не растут карьерно, так как поставить на их место некого, потому что никто не знает специфику этого проекта, а рейнджер не даёт ничего делать другим в этом проекте. Несмотря на дополнительные издержки в виде коммуникации, старайтесь работать в команде — это упрощает работу как вам, так и проекту.</p><h2>Не практиковаться ежедневно</h2><p>Писать код нужно ежедневно, как и делать утреннюю зарядку. Любое действие должно обязательно сопровождаться практикой: изучил теорию — попрактиковался. Причем важно также упражняться в тех вещах, которые прямо сейчас в проекте не задействованы, но которые позволяют держать пальцы «разогретыми».</p><h2>Не делать физическую зарядку</h2><p>Если тело находится в тонусе, то и мозг находится в тонусе и активно снабжается кислородом. Поэтому важно двигаться, заниматься спортом.</p><h2>Пытаться сначала всё до конца продумать в голове и только потом писать</h2><p>Необходимо постоянно пытаться что-то делать, действовать методом проб и ошибок. Должны быть короткие циклы разработки: придумал, написал, попробовал. Понравилось — оставил, не понравилось — придумал что-то новое.</p><h2>Цепляться за первоначальный вариант</h2><p>Пытаться бесконечно улучшать, стиснув зубы, доводить до конца одну идею, несмотря на то, что уже понимаешь, что не очень хорошо получается, — это плохая привычка. С одной стороны, конечно, такое упорство похвально, но я рекомендую начинать с нескольких вариантов и каждый из них доводить до этапа, на котором становится понятно, какой лучше. Тогда плохой оставляете и работаете с хорошим.</p><h2>Не общаться с себе подобными, замыкаться в себе</h2><p>Нужно обязательно посещать мероприятия, участвовать в жизни сообщества, желательно поработать на open-source-проектах, с другими разработчиками, послушать замечания других о своём коде — это очень сильно помогает развиваться.</p><p>В каждом языке может быть своя специфика, но есть универсальные ошибки, которые верны для любого языка программирования.</p><h2>Код без комментариев</h2><p>Не обязательно комментировать каждую команду, но желательно прописывать основные моменты. Это важно, так как с кодом, возможно, будут работать и другие программисты, которым желательно не тратить много времени на разбор чужих программ.</p><h2>Ненужное усложнение</h2><p>В программировании почти всегда одни и те же действия можно сделать несколькими способами. Желательно выбирать не только самый эффективный из них, но и следить за тем, чтобы код не был избыточно усложнён.</p><h2>Неоттестированный код</h2><p>Очень часто бывает так, что начинающий программист добавляет в проект код, который работает только при каких-то определённых условиях и не работает при других. Перед добавлением изменений всегда лучше убедиться, что программа знает, как себя вести при любых действиях пользователя.</p><h2>Незнание алгоритмов и структур данных</h2><p>Нужно знать базовые алгоритмы и быть осведомлённым о наличии разнообразных алгоритмов не только для того, чтобы пользоваться готовыми решениями, но и для того, чтобы понимать сильные и слабые стороны своих программ и учиться ускорять их работу. Также важно правильно выбирать структуры данных — это залог быстрой работы программы.</p><h2>Неумение пользоваться IDE</h2><p>Не обязательно знать среду разработки в совершенстве, достаточно знать то, что пригодится в ежедневной работе. Например, для программиста Python умение пользоваться такими возможностями PyCharm, как удалённая отладка, поддержка Git, автоматический рефакторинг, горячие клавиши для повторяющихся действий, значительно ускорит работу с кодом и позволит не тратить время на второстепенные вещи.</p><h2>Неумение пользоваться Git</h2><p>К чему только не прибегают программисты, не желающие пользоваться системами контроля версий. В лучшем случае они хранят версии своего кода в папках с трудночитаемыми названиями, а иногда и вовсе редактируют программу, никак не фиксируя свои изменения и не сохраняя предыдущие версии кода.</p><h2>Неумение искать информацию</h2><p>Для развития в программировании нужно использовать разнообразные источники: курсы, книги, сайты для программистов, поисковые системы. Типичная ситуация: новичок в программировании покупает учебник, спотыкается на какой-либо теме и в итоге бросает занятия.</p><p>Вообще, для книг можно применять такое правило: покупать по одной теме не одну, а хотя бы три книги, и если хотя бы одна из них помогла разобраться, то это уже хорошо.</p><p>Самая плохая привычка — не вносить изменения в документацию. Проектное время должно расходоваться преимущественно на создание кода. И начинающие программисты часто задаются вопросом: зачем вообще нужна документация и строгая нумерация в ней, зачем нужны отметки об исполнении не только в Jira, но и на бумаге. По итогу человек, не придерживающийся технологии работы с кодом, может украсть 2–3 дня квалифицированной команды, которая, конечно, разберётся, что и как сделано, но слов ругательных скажет много.</p><p>Лечение — только в отстроенном технологическом процессе. Чем «махровей» энтерпрайз, тем меньше он доставляет головной боли. Кстати, хорошие продукты, производимые под свободными лицензиями, это давно поняли и тоже стремятся к разумному и документированному взаимодействию.</p><p>Ещё про костыли. Есть наборы продуктов, которые без костылей не запустить никак. Более того, сочетание версий костылей библиотеках Python 2 и 3 — отдельная тема на многих конференциях для разработчиков. Даже крупные вендоры выпускают патчи при смене парадигмы костылей. Поэтому исправление плохой привычки следующее: используешь костыль — отладь, оттестируй, выступи в группе или на форуме. Можешь создать бескостыльную систему — делай. И документируй. Не будь отрицательным примером!</p><p>Теперь касательно велосипедов. Конечно, на собеседовании и тестовых примерах можете демонстрировать хоть знание Кнута, Ахо, Страуструпа или иных гуру. Но как только написано «алгоритм должен/может/обязан», — конец творчеству, конец велосипедам. Берём мануалы по языку, библиотекам, на худой конец идём на курсы. Мы физически не можем закладывать логическую бомбу внутрь промышленного изделия. Иначе восьмёрки на колесах нашего велосипеда могут подорвать любой бэкенд. Пример: на ряде промышленных систем чёрный цвет на снимках фотограмметрии стал серым, так как никто не мог додуматься, что при смене архитектуры чипа может поменяться алгоритм закраски фона белым цветом. Тут нужна комплексная борьба — так как графические системы ваяются чуть ли не на коленках. И велосипед одних может врезаться в дерево других.</p><p>У большинства ошибок начинающего программиста общая причина — привычка сразу браться за работу без предварительного обдумывания задачи. Как это проявляется?</p><ul><li>Услышал задачу и сразу побежал делать. Позже выясняется, что заказчик пришёл с собственной идеей того, как решить некоторую проблему. Если бы разработчик спросил, что за проблему нужно решить, то сразу бы увидел, что есть более удачное и, при этом, более простое решение.</li><li>Услышал задачу и побежал писать код. Через неделю мучений выясняется, что можно не писать своё решение, а найти и интегрировать готовую библиотеку.</li><li>Услышал задачу, придумал решение и побежал реализовывать. Через пару дней наткнулся на проблему, наличие которой было бы очевидно через 10 минут размышлений о деталях придуманного решения.</li><li>Получил задачу и побежал делать. А можно было бы подумать о том, что задача для компании типичная, её наверняка уже решали — и за полдня найти коллегу с опытом и готовым решением.</li><li>Наткнулся на проблему при отладке, исправил — возникло две новых. Исправил их — возникло ещё несколько. И так много раз. Если немного подумать о причинах возникновения исходной проблемы, то можно было бы найти архитектурный дефект и разбираться уже с ним, а не с симптомами.</li><li>Увидел плохой код — сразу начал его рефакторить. А потом связанный код. А потом код, связанный с кодом из предыдущего шага — до бесконечности, так и не получив полезный результат из-за невозможности стабилизировать изменения. А можно было бы подумать об актуальности рефакторинга в этом месте и лучшем способе его сделать — и спланировать такие изменения, на которые точно хватит времени.</li></ul><p>С опытом привычка сначала думать, а потом делать появится сама. Но лучше осознанно обращать на это внимание — так можно дойти до образа мышления опытного разработчика гораздо быстрее.</p><p>Во-первых, мы не любим документировать. Частый девиз — код и есть лучшая документация. А когда нужно допилить систему спустя несколько лет, и автор давно уже недоступен, начинаются проблемы.</p><p>Второе пагубное влечение — мы не любим читать требования. Они такие длинные, такие детальные, в них порой всё так подробно написано и разжёвано… но мы-то лучше знаем, как надо или просто лень… Ах да, если что — перепишем. Как следствие из этого недуга вытекает ещё один: «Я всё понял, завтра уже закончу и выдам код». Спешка в нашем деле лишняя, а постановка задачи не зря занимает столько страниц.</p><p>Ещё одно довольно странное явление встречается уже реже: не пользоваться средствами отладки и профилирования. Я не спорю, ошибки, конечно, можно найти, вдумчиво рассматривая код. Но всё же, все современные IDE обладают весьма богатыми средствами отладки. Это удобно и практично.</p><p>Следующая пагубная для окружающих привычка — «а я так привык» или «мой код правильнее»… Сначала я думал, что это бывает только «по молодости», но, к сожалению, встречаю такое и у более опытных коллег. Поэтому не принимать во внимание текущие стандарты и локальные практики заказчика становится той самой плохой привычкой.</p><p>Первая и, пожалуй, одна из самых частых привычек, которая характерна для молодых программистов, это «я всё сделаю сам». Стесняясь спросить совета у более опытного коллеги, начинающий кодировщик потихоньку «закапывается». Или наоборот, быстро перекладывая ответственность за свой код, не анализируя последствия конкретной реализации, не представляя итоговой структуры, сдаёт сырой материал. Любой хороший кодер, а не только начинающий, должен проверять сразу несколько кейсов, уметь прогнозировать свою функциональность и развивать абстрактное мышление, исключая в своей практике хардкод.</p><p>Другая ситуация, когда айтишник в силу небольшого опыта знает ограниченный инструментарий, пользуется только им и ленится разобраться с прикладными возможностями. Показателем лени является и нежелание следовать принятым в команде стандартам. По факту, всё это только затягивает и усложняет процесс написания кода.</p><p>Одна из вредных привычек начинающих программистов, которую мы сразу стараемся искоренять у нас в компании — нежелание или неумение молодых специалистов писать тесты. Уже на этапе разработки они очень помогают быстро находить ошибки и уменьшают затраты на сопровождение кода.</p><p>Конечно, второй Билл Гейтс для компании ценный кадр. Но когда новичок с высоким самомнением считает себя «богом кодинга», это значительно снижает его шансы на успех. Даже если он тысячу раз талантлив, его неспособность коммуницировать на равных с другими людьми может убить все старания, породить кучу костылей в коде, привести к багам, а ещё хуже — к недопониманию в команде.</p><p>Каждый программист индивидуален, и у каждого есть те или иные плохие привычки. Зачастую они проявляются в тот момент, когда программист начинает работать над проектом. Чтобы разобраться, что мешает, разделим плохие привычки на три группы: технические, личные, коммуникативные, — и рассмотрим каждую из них.</p><p>К техническим плохим привычкам можно отнести: желание иметь слишком хороший компьютер для разработки и вместо целевого решения делать костыль в коде. А также нежелание смотреть исходники и изучать документацию.</p><p>Чтобы этого избежать, необходимо постоянно технически развиваться, изучать разные проекты с простой и сложной архитектурой. Применять best practices, паттерны. Думать о производительности с учётом мощностей пользовательских рабочих мест.</p><p>К личным стоит отнести привычку откладывать на потом, очень долго концентрироваться на одной задаче, думать, что ты всё знаешь. Не придавать внимания качеству кода: неймингу, связанности, переиспользованию, производительности, тестированию кода. Иметь плохой баланс личной жизни и работы.</p><p>Как избежать — развивать тайм менеджмент, чувство перфекционизма. Стараться выслушивать чужие точки зрения, и обсуждать свои. Завести интересы вне сферы работы, это помогает переключаться и в итоге эффективнее работать.</p><p>И, наконец, к коммуникативным стоит отнести следующие плохие привычки: не обсуждать решение с другими разработчиками. Быть высокомерным, деспотичным, не учитывать при общении личные качества членов команды. Не разбираться в чужом коде, не проводить ревью и анализ своего кода. Не признавать свои ошибки. Не общаться с коллегами. Быть недружелюбным.</p><p>Как избежать этого — проводить ревью своего и чужого кода. Больше работать с чужим кодом. Обсуждать с коллегами проблемы, возникшие при разработке. Быть дружелюбным.</p><p>Многие из описанных вредных привычек достаточно общие. Другие, наоборот, встречаются только у программистов. Но так или иначе их следует избегать. Помните, что отношение к коду, коллегам, проекту так же важно, как и ваш опыт и знания. Поддержание на высоком уровне всех этих составляющих даст вам больше профита как в личном, так и профессиональном плане, чем развитие отдельных качеств.</p><h2>Итак, какие плохие привычки бывают у программистов и что с ними делать?</h2><p>Программисты порой:</p><ul><li>Не документируют код и/или не вносят изменения в документацию.</li><li>Пишут лишние велосипеды.</li><li>Не практикуются в написании кода регулярно.</li><li>Игнорируют физические активности.</li><li>Пытаются продумать все детали программы перед её реализацией.</li><li>Сразу пробуют решить задачу, не узнав о всех нюансах вроде своего видения решения задачи у заказчика.</li><li>Не читают требования.</li><li>Пытаются отрефакторить плохой код, не вникая, почему его написали именно так.</li><li>Излишне усложняют код.</li><li>Не умеют искать информацию.</li><li>Не знают и доли возможностей своей IDE.</li><li>Не общаются с единомышленниками и замыкаются в себе.</li><li>Игнорируют общепринятые практики и делают всё в стиле «я так привык» или «я лучше знаю».</li><li>Пытаются сделать всё сами.</li></ul><p>Что с этими привычками делать? Не переставайте развиваться. Вам не обязательно писать на всех фреймворках, пихать в каждый проект все паттерны проектирования и т. д., но знание того, какие вещи вообще существуют в мире программирования и как их реализуют другие люди, может сильно вам помочь. Для каких-то привычек нужно просто перебороть и заставить себя сделать «как надо». Ну а если вы где-то работаете, то некоторые хорошие привычки вам привьют волей-неволей. Кроме того, в этом деле может помочь опыт коллег, которые проходили через то же самое.</p><p>Напоминаем, что вы можете <a href="https://docs.google.com/forms/d/e/1FAIpQLSdSanNvlfPRrSyQWfnoGPflSVwO4KctnjOdEKHzuxjCmFX2dA/viewform">задать свой вопрос</a> экспертам, а мы соберём на него ответы, если он окажется интересным. Вопросы, которые уже задавались, можно найти в списке выпусков <a href="https://tproger.ru/experts/">рубрики</a>. Если вы хотите присоединиться к числу экспертов и прислать ответ от вашей компании или лично от вас, то пишите на <a>experts@tproger.ru</a>, мы расскажем, как это сделать.</p>]]></content:encoded>
    </item>
    <item>
      <title>Рефакторим код на Python с помощью тестов</title>
      <link>https://tproger.ru/translations/python-refactoring-with-tests</link>
      <comments>https://tproger.ru/translations/python-refactoring-with-tests?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/python-refactoring-with-tests</guid>
      <description><![CDATA[<p>Тесты снижают число новых багов при переработке непротестированного и устаревшего кода. Пошаговый пример рефакторинга на Python и приёмы работы с ним.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/python-refactoring-with-tests">Рефакторим код на Python с помощью тестов</a>»</p>]]></description>
      <category><![CDATA[Python]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Sun, 16 Sep 2018 15:47:12 GMT</pubDate>
      <content:encoded><![CDATA[<p>В статье описан пошаговый рефакторинг кода с помощью тестов. Рефакторинг опасен при работе с непротестированным или устаревшим кодом, но тестирование поможет уменьшить количество внедряемых багов и при определённой доле везения избежать их вовсе.</p><p>Рефакторинг не для слабаков и требует двойных усилий: 1) нужно понимать код, который написал кто-то другой или ты сам в прошлом; 2) с умом упрощать или переносить куски кода (читай улучшать код). В рефакторинге, как и в программировании, есть свой свод правил и приёмов, который можно описать как смесь из техники, интуиции, опыта и риска.</p><p>Всё-таки программирование – это искусство.</p><h2>Исходные данные</h2><p>В качестве примера будем использовать сервис, предоставляющий API и отдающий данные в формате JSON, а именно список из элементов, как показано здесь:</p><p>После того, как мы преобразуем объект JSON в питоновскую структуру, то получим набор словарей, где коллекция age – целое число, остальные – строки.</p><p>Потом кто-то дописал класс, который рассчитывает некоторые статистические данные по исходным. Класс называется DataStats и содержит единственный метод stats(), входными параметрами которого являются данные, полученная от сервиса (JSON), и два целых числа iage и isalary. Согласно документации, эти параметры – исходный возраст и исходная зарплата, используемые для вычисления среднегодовой надбавки к зарплате.</p><p>Код класса:</p><h2>Цель</h2><p>Легко заметить некоторые проблемы в классе, описанном выше. Самые заметные:</p><ul><li>Класс использует один метод и не содержит __init__(). Его можно заменить на единственную функцию без потери функционала.</li><li>Метод stats() слишком большой и выполняет много разрозненных задач, что усложняет последующую отладку.</li><li>Много повторяющегося кода, по крайней мере несколько строк очень похожи. Например, две очень похожих операции '£' + str(max(salaries)) и '£{}'.format(str(min(salaries))), или две строки начинаются с salaries =, или несколько конструкторов списков.</li></ul><p>Мы собираемся использовать этот код в нашем Amazing New Project™, так что хотелось бы исправить эти недостатки.<br />Однако класс работает идеально, используется в производстве долгие годы и не содержит известных багов. Мы хотим написать код лучше, сохраняя при этом функционал, то есть сделать рефакторинг.</p><h2>Путь</h2><p>Я хочу показать, как безопасно отрефакторить такой класс, используя тесты. Этот способ отличается от разработки через тестирование (TDD), хотя они похожи. Используемый класс разрабатывался без помощи TDD, и для него нет никаких тестов, но тем не менее их можно использовать, чтобы удостовериться, что работа класса осталась прежней. Такой способ стоит называть рефакторинг через тестирование (TDR – test driven refactoring).</p><p>Идея TDR проста. В первую очередь, разрабатываются тесты, которые проверяют работу какого-то кода, лучше маленькой части с чётко определённой областью деятельности и выходными данными. Позднее юнит-тестирование, которое симулирует, что автор кода должен был сделать (кхм, это же ты несколько месяцев назад…).</p><p>Как только юнит-тесты будут готовы, можно смело редактировать код, зная, что работа новой версии кода будет такой же, как и у предыдущей. Как можно догадаться, эффективность метода напрямую зависит от качества написанных юнит-тестов, именно поэтому рефакторинг сложен.</p><h2>Предостережения</h2><p>Прежде чем начнём наш первый рефакторинг, выскажу два замечания. Первое: код в примере легко отрефакторить. Здесь нет необходимости соблюдать принципы ООП, но я пошел этим путём, чтобы продемонстрировать технику рефакторинга для упаковщиков.</p><p>Второе: в чистом TDD не рекомендуется тестировать внутренние методы, которые не формируют публичные API объекта. В целом, мы выделяем такие объекты, добавляя нижнее подчёркивание перед названием. Причина в том, что TDD подразумевает, что объекты формируются исходя из ООП, которое рассматривает объекты как результат его работы, а не как структуру. Таким образом, в тестировании нас интересуют публичные методы.</p><p>Однако стоит отметить, что иногда сложно сделать публичный метод, так как в методе запутанная логика, которую мы хотим протестировать. По моему мнению, совет по TDD должен звучать так: «Тестируйте внутренние методы, только если в них содержится неочевидная логика».</p><p>Когда же идёт рефакторинг, мы разбираем существующую структуру и чаще всего преобразуем её в набор приватных методов, помогающих выделять и обобщать части программного кода. Мой совет, в таких случаях стоит тестировать полученные методы, это позволяет быть более уверенным в том, что ты сделал. С опытом придёт понимание, какие тесты нужны, а какие можно опустить.</p><h2>Подготовка к тестированию</h2><p>Клонируем этот репозиторий и создаём виртуальную рабочую среду. Активируем её и устанавливаем необходимые пакеты.</p><p>pip install -r requirements.txt</p><p>Репозиторий уже содержит конфигурационный файл для pytest, который нужно модифицировать, чтобы избежать ввода вашей виртуальной среды. В нём нужно поправить параметр norecursedirs, добавив имя виртуальной среды, которая только что была создана. Я обычно даю имя виртуальное среде с префиксом venv, поэтому её название имеет вид venv*.</p><p>На данном этапе из родительской директории репозитория, которая содержит pytest.ini, должна запускаться команда pytest -svv, результат будет походить на то, что представлено ниже:</p><p>Этот репозиторий содержит две ветки. В ветке master, в которой вы сейчас находитесь, содержится начальная настройка, в ветке develop – конечный результат рефакторинга. Каждый шаг из этого поста имеет свой коммит с соответствующими правками.</p><h2>Шаг 1. Тестируем конечный результат</h2><p>Коммит: 27a1d8c</p><p>Когда начинаешь рефакторить систему, вне зависимости от её размера, нужно обязательно протестировать конечный результат её работы. В этом случае систему стоит рассматривать как чёрный ящик (т.е. вы не знаете, что находится внутри) и проверить внешнее поведение. В этом случае можно написать тест, который инициализирует класс и запускает метод с тестовыми данными, возможно реальными, и проверяет выходные данные. Естественно, мы напишем тест с действующими выходными данными, возвращаемыми методом, поэтому тест проходит автоматически.</p><p>Запросив данные у сервера, мы получаем следующее:</p><p>и, вызвав метод stats() с выходными данными, где iage = 20 и isalary = 20000, получим следующий JSON:</p><p>Предупреждение: в примере я использую очень короткий список реальных данных (3 словаря). В реальном рефакторинге я бы использовал много разнообразных данных, чтобы быть уверенным, что это не пограничный случай.</p><p>Тест:</p><p>Как было сказано ранее, тест явно проходит, так как был искусственно сконструирован из результатов работы неизменённого кода.<br />Ну что ж, этот тест очень важен! Сейчас мы знаем, что если своими изменениями кода мы нарушим его алгоритм работы, то хотя бы один тест не пройдёт.</p><h2>Шаг 2. Избавляемся от JSON</h2><p>Коммит: 65e2997</p><p>Метод возвращает данные в формате JSON и, посмотрев код, можно заметить, что форматирование происходит с помощью функции json.dumps().</p><p>Структура кода, где code_part_2 зависит от code_part_1:</p><p>Первый рефакторинг будет происходить следующим образом:</p><ol><li>Мы напишем тест test__stats() для метода _stats(), который будет возвращать данные в формате питоновской структуры. Позже можно будет вручную сформировать JSON или выполнить json.loads() в питоновском скрипте. Тест не проходит.</li><li>Мы продублируем код метода stats(), который выводит данные в новый метод _stats(). Тест проходит.</li></ol><p>Уберём дублирующийся код в stats() и заменим его вызовом _stats():</p><p>Сейчас мы сможем отрефакторить первоначальный тест test_json(), который мы написали, но это более сложные изменения, и я оставлю их для другого раздела.</p><p>Сейчас код нашего класса выглядит следующим образом:</p><p>И у нас есть два теста, проверяющих правильность его выполнения.</p><h2>Шаг 3. Рефакторим тесты</h2><p>Коммит: d619017</p><p>Очевидно, что список словарей test_data будет использован в каждом проводимом тесте, так что сейчас самое время перенести его в глобальную переменную. Нет смысла использовать фикстуру (fixture), так как тестовые данные статичны.</p><p>Также можно вынести выходные данные в глобальную переменную, но предстоящие тесты не используют весь выходной словарь, поэтому мы можем отложить это решение.</p><p>Теперь набор тестов выглядит так:</p><h2>Шаг 4. Изолируем подсчёт среднего возраста</h2><p>Коммит: 9db1803</p><p>В разработке ПО главной задачей является изолирование независимых функций. Таким образом, наш рефакторинг должен разбить существующий код на маленькие разделённые функции.</p><p>Выходной словарь содержит пять ключей, которым соответствуют значения либо подсчитанные «на лету» (для avg_age и avg_salary), либо по коду метода (для avg_yearly_increase, max_salary и min_salary). Мы можем начать замену кода, который вычисляет значение каждого ключа выделенными методами, пытаясь изолировать алгоритмы.</p><p>Для изоляции кода нужно в первую очередь его продублировать, поместив копию в выделенный метод. Так как мы рефакторим с помощью тестов, то нулевым шагом будет написать тест для этого метода.</p><p>Мы знаем, что метод должен вернуть 62, поскольку это значение возвращает оригинальный метод stats(). Обратите внимание, что нет смысла передавать переменные iage и isalary, поскольку они не используются в исправленном коде.</p><p>Тест не пройден, так что мы можем послушно пойти и продублировать код, используемый для подсчёта avg_age:</p><p>Как только тест проходит, мы можем заменить скопированный код в _stats() на вызов функции _avg_age():</p><p>Проверяем, проходит ли тест. Здорово! Мы изолировали первую функцию и написали уже три теста.</p><h2>Шаг 5. Изолируем подсчёт средней зарплаты</h2><p>Коммит: 4122201</p><p>Ключ avg_salary работает так же, как и avg_age с другим кодом. Таким образом, процесс рефакторинга такой же, как и в предыдущем шаге, а результатом будет новый тест test__avg_salary():</p><p>Новый метод _avg_salary():</p><p>Новый вид возвращаемого значения:</p><h2>Шаг 6. Изолируем алгоритм ежегодного повышения зарплаты</h2><p>Коммит: 4005145</p><p>Оставшиеся три ключа подсчитываются алгоритмами, которые длиннее одной строки и не могут быть записаны напрямую в описание словаря. Однако процесс рефакторинга не особо изменяется: как и раньше мы сначала тестируем вспомогательный метод, затем определяем его посредством дублирования и, наконец, вызываем вспомогательный метод, удаляя продублированный код.</p><p>Для среднегодового повышения зарплаты у нас новый тест:</p><p>Новый метод, который проходит тест:</p><p>Новая версия метода _stats():</p><p>Обратите внимание, что мы не решаем проблему дублирования кода, кроме того, что вводим для рефакторинга. Первое, к чему мы стремимся, это полностью изолировать независимые функции.</p><h2>Шаг 7. Изолируем подсчёт максимальной и минимальной зарплаты</h2><p>Коммит: 17b2413</p><p>Во время рефакторинга все следует делать поочерёдно, но ради краткости я покажу результат двух шагов за раз. Читателям я рекомендую выполнить их как самостоятельные шаги, как я и сделал при написании кода, который публикую ниже.</p><p>Новые тесты:</p><p>Новые методы в классе DataStats:</p><p>И метод _stats() сейчас очень короткий:</p><h2>Шаг 8. Избавляемся от повторяющегося кода</h2><p>Коммит: b559a5c</p><p>Сейчас, когда у нас есть главные тесты, мы можем изменять код различных вспомогательных методов. Они достаточно малы, что позволяет делать изменения без написания дополнительных тестов. Это применимо к данному случаю, но в общем нет такого понятия как «достаточно маленький» так же, как нет реального определения «юнит теста». Вообще, вы должны быть уверены, что изменяемая часть кода покрыта тестами. Если это не так, то следует добавить один или несколько тестов, пока вы не почувствуете себя достаточно уверенно.</p><p>Два метода _max_salary() и _min_salary() имеют много общего кода, хоть и второй более краткий.</p><p>Я начну с того, что объявлю пороговую переменную threshold во второй функции. После любых изменений я запускаю тесты, чтобы проверить что внешнее поведение кода не изменилось.</p><p>Теперь очевидно, что функции, кроме min() и max(), одинаковы. Они до сих пор используют разные имена переменных и разный код для формирования порога, так что в первую очередь я их сравняю, скопировав код из _min_salary() в _max_salary() и изменив min() на max().</p><p>Теперь я могу создать ещё одну вспомогательную функцию _select_salary(), которая продублирует этот код и примет в качестве одного из аргументов функцию, используемую вместо min() или max(). Как я делал ранее, я сначала дублирую код, а затем убираю повторы, заменяя их на вызов новой функции.</p><p>Затем я заметил дублирующийся код в _avg_salary() и _select_salary():</p><p>Я решил вынести общий алгоритм в метод _salaries(). Как и раньше, я сначала написал тест:</p><p>Затем применил метод:</p><p>И в итоге заменил дублирующийся код вызовом нового метода:</p><p>Пока я делал изменения, я заметил, что функция _avg_yearly_increase() содержит такой же код и исправил её.</p><p>В этот момент было бы полезно входные данные поместить внутри класса и использовать как self.data, вместо того, чтобы передавать её во всех методах класса. Однако это нарушит API класса, так как в текущий момент DataStats инициализирован без данных. Позже я покажу, как вводить изменения, которые могут нарушить API и коротко обрисую проблему. Сейчас же я продолжу изменять класс без изменений внешнего интерфейса.</p><p>Похоже, что age имеет такую же проблему с повторением кода, как и salary, поэтому таким же образом я введу метод _ages() и изменю методы _avg_age() и _avg_yearly_increase().</p><p>Кстати, говоря о _avg_yearly_increase(), код данного метода дублируется в методах _avg_age() и _avg_salary(), так что стоит его заменить вызовами двух функций. Поскольку я перемещаю код между существующими методами, мне не нужны дальнейшие тесты.</p><h2>Шаг 9. Рефакторинг повышенной сложности</h2><p>Коммит: cc0b0a1</p><p>У начального класса не было метода __init__() и, таким образом, отсутствовала часть инкапсуляции ООП. Не было причин оставлять класс, так как метод stats() можно было легко извлечь и представить в виде простой функции.</p><p>Это стало более очевидно, когда мы отрефакторили метод, потому что сейчас у нас есть 10 методов, которые принимают data как параметр. Было бы неплохо загрузить входные данные во время инициализации метода, а затем получать доступ к ним как self.data. Это значительно улучшит читаемость класса и оправдает его существование.</p><p>Однако, если мы добавим метод, требующий параметры, мы изменим API класса, нарушив совместимость с любым другим кодом, который его импортирует и использует. Поскольку мы хотим сохранить API без изменений, нам нужно придумать обходной путь, который позволит использовать преимущества нового чистого класса, но в то же время не нарушит API. Это не всегда достижимо, но в этом случае проблему поможет решить адаптер (или упаковщик).</p><p>Цель состоит в том, чтобы текущий класс сделать соответствующим новому API, а затем написать упаковщик, который адаптирует этот класс под требования старого API. Стратегия не очень отличается от той, что мы использовали ранее, только в этот раз мы будем работать с классами, а не методами. Огромным усилием моего воображения я назвал новый класс NewDataStats. Простите, но иногда нужно просто сделать работу.</p><p>Первым делом, как это часто бывает с рефакторингом, будет продублировать код, а когда мы вставим новый код, нам нужны будут тесты, которые его проверят. Тесты будут такие же, как и ранее, поскольку новый класс должен выполнять тот же функционал, что и раньше, так что я просто создал новый файл test_newdatastats.py и начал создавать первый тест test_init().</p><p>Этот тест не проходит, и код, реализующий класс, очень прост:</p><p>Теперь я могу начать повторяющийся процесс:</p><ol><li>Я скопирую один тест из DataStats и адаптирую его для NewDataStats.</li><li>Я скопирую код из DataStats в NewDataStats, адаптируя его под новое API, и удостоверюсь, что он проходит тест.</li></ol><p>Итеративное удаление методов из DataStats и замена их вызовом из NewDataStats будут излишними. В следующем разделе я покажу, почему и как этого можно избежать.</p><p>Пример результата тестов для NewDataStats:</p><p>И код, который проходит тест:</p><p>После этого я заметил, что сейчас методы похожие на _ages() не нуждаются в входных параметрах, я могу преобразовать их в свойства, соответственно меняя тесты.</p><p>Настало время заменить методы в DataStats вызовом из NewDataStats. Мы можем это сделать пошагово, метод за методом, но что нам на самом деле нужно, так это заменить метод stats().</p><p>И поскольку все другие методы больше не используются, мы можем безопасно удалить их, не боясь, что тесты не пройдут. В случае с тестами, удаление методов приведёт к тому, что многие тесты DataStats не пройдут, так что их тоже следует удалить.</p><h2>Послесловие</h2><p>Если вам интересна тема рефакторинга, то стоит почитать классику – Мартин Фаулер «РЕФАКТОРИНГ. Улучшение существующего кода», в этой книге собран набор шаблонов рефакторинга. Справочный язык – Java, но шаблоны легко применяются и на Python.</p>]]></content:encoded>
    </item>
    <item>
      <title>Не комментируйте свой код — перепишите его</title>
      <link>https://tproger.ru/translations/dont-comment-your-code-rewrite-it</link>
      <comments>https://tproger.ru/translations/dont-comment-your-code-rewrite-it?scrollTo=tproger-comments</comments>
      <dc:creator><![CDATA[Типичный программист]]></dc:creator>
      <guid isPermaLink="true">https://tproger.ru/translations/dont-comment-your-code-rewrite-it</guid>
      <description><![CDATA[<p>Обилие комментариев чаще указывает на плохо написанный код: программист рассказывает, как поменял отношение к комментированию по мере опыта.</p><p>— Читать дальше «<a rel="follow" href="https://tproger.ru/translations/dont-comment-your-code-rewrite-it">Не комментируйте свой код — перепишите его</a>»</p>]]></description>
      <category><![CDATA[Советы]]></category>
      <category><![CDATA[Рефакторинг]]></category>
      <category><![CDATA[Кодстайл]]></category>
      <category><![CDATA[Переводы]]></category>
      <slash:comments>0</slash:comments>
      <pubDate>Fri, 06 Feb 2015 12:46:29 GMT</pubDate>
      <content:encoded><![CDATA[<p>Комментирование кода — это один из аспектов, к которому я изменил своё отношение в процессе профессионального развития. Когда я был еще новичком, я считал, что нужно комментировать чуть ли не каждую строчку кода, каждый даже самый небольшой его участок. Казалось, это поможет быстрее разобраться в том, что и где происходит. Комментарии ведь на обычном человеческом языке, а код такой сложный и непонятный. Но позже я осознал, что это был просто плохо написанный код, и вся моя головная боль не от плохих комментариев или их отсутствия, а от сотен невнятных строк подряд в одной функции и переменных в стиле x, y, или i.</p><p>Я считаю, что комментарии относятся к признакам «<a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%B4_%D1%81_%D0%B7%D0%B0%D0%BF%D0%B0%D1%88%D0%BA%D0%BE%D0%BC">кода с запашком</a>» и должны использоваться крайне экономно и только там, где они на самом деле нужны.</p><h3>Так что же нужно комментировать?</h3><p>Вообще, я не сказал, что комментарии не нужны совсем, в программировании довольно редко используются какие-либо абсолютные соглашения. Вы должны быть уверены, что есть на самом деле весомая причина использования пояснений в коде. Я использую их как предупреждающие знаки о том, что программисту стоит обратить особое внимание на этот участок. Скорее всего, он чем-то опасен или делает какую-то неочевидную вещь. Можно ли сказать то же самое про любой кусок кода, который чем-то сложен и сильно запутан? Да, можно! По этой причине я часто использую комментарии в legacy-коде, над которым не ведётся активная работа. Т.к. из-за внешних бизнес-условий мы не всегда можем проводить рефакторинг так часто, как хотелось бы, да и иногда лучше просто не трогать то, что и так работает, то комментарии могут быть полезны. Я считаю, что в данном случае это необходимое зло.</p><h3>Почему же комментарии — это зло?</h3><p>Короткий ответ в том, что они часто врут нам. Они говорят, что делает данный код и часто могут не изменяться в последующих ревизиях. Например, есть функция, которая возвращает string, и я так и написал в комментариях. Но затем потребовалось изменить её, и какой-то другой программист внёс быстрые правки, поменяв тип возвращаемого значения на int, но забыл обновить комментарии. Это довольно распространённый случай. Вот мы и пришли к ситуации, когда комментарии говорят одно, а происходит на самом деле совсем другое.</p><p>Мы знаем, что правда всегда на стороне кода. Ведь это именно то, что будет происходить в реальном продукте. Так зачем же читать комментарии, которые потенциально могут быть ошибочны и тратить время на них, когда можно просто посмотреть на код и понять, что он делает?</p><blockquote>Код никогда не лжёт, комментарии иногда делают это. — Ron Jeffries</blockquote><h3>Как писать код так, чтобы комментарии были не нужны?</h3><p>В проектах, которые активно разрабатываются, я стараюсь писать код так, чтобы он выражал то, что он должен делать. Это, пожалуй, то, на что я трачу большую часть своего времени. Иногда мне со значительным трудом даётся выбор названий. Обычно я стараюсь выбирать такое имя для функции или класса, которое напрямую отражает то, что они делают, а позже переименовываю, если это необходимо.</p><p>Отрывок выше является хорошим примером выразительного кода. Даже ребёнок сможет понять, что это собака, и она может лаять. Конечно, в большинстве случаев реальный код намного сложнее и далеко не так очевидно, как написать его правильно, но общие принципы везде одинаковые. Старайтесь давать имена сущностям так, как будто вам надо будет объяснить это ребёнку. В данном примере мы можем описать код как «собака умеет лаять». Точно так же можно сказать «пользователь может залогиниться» или «пользователь может изменить данные своего профиля».</p><blockquote>Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живёте. — Martin Golding</blockquote><p>Эта цитата всегда казалась мне забавной, пока я сам не столкнулся с сопровождением кода, написанного просто ужасно. Мне на самом деле хотелось как следует врезать его автору за то, что он сделал код настолько сложным для сопровождения. Простые изменения, которые заняли бы минут 20 в коде с хорошей архитектурой, растянулись на 4 часа.</p><h3>Внимательно относитесь к именам переменных, функций и классов</h3><p>Нет ничего необычного в том, чтобы в процессе разработки изначальное название функции или переменной изменялось несколько раз. Особенно, если цикл разработки продолжается более, чем один день. Если мне пришла в голову мысль о том, как можно более правильно назвать что-либо, я вернусь к этому участку кода и потрачу время на его изменение. Всякий раз, когда мне кажется, что комментарий был бы здесь очень кстати, я возвращаюсь на шаг назад и еще раз смотрю на написанный код. Иногда даже консультируюсь с коллегой, чтобы понять, насколько комментарии всё-таки оправданы здесь. Иногда комментарии побеждают, но обычно, если это не какой-то очень специфичный участок кода, они всё-таки не нужны.</p><blockquote>Любой дурак может написать код, понятный компьютеру. Хорошие программисты пишут код, понятный людям. — Martin Fowler</blockquote><p>В этой цитате Мартин Фаулер описал всю суть того, почему нужно избегать комментариев. Ваш код должен быть настолько хорошо читаемым, что любой другой программист смог бы разобраться в нём. А комментарии просто кричат о том, что вы не сможете понять эту строчку без дополнительных пояснений. Вот почему я избегаю их, и вам советую поступать так же.</p><p>В следующий раз, когда вы будете писать код, постарайтесь делать это без комментариев вообще. Затем, через некоторое время, взгляните на программу и подумайте, насколько легко её читать. Если возникают трудности и вы не можете понять, что вы сами же написали без комментариев, просто выкиньте всё и перепишите с нуля.</p><p>Перевод статьи «Don’t Comment Your Code – Rewrite It»</p>]]></content:encoded>
    </item>
  </channel>
</rss>