Стресс-тест Biome: как Vercel улучшил правило noFloatingPromises для поиска необработанных Promise
Необработанные Promise — одна из самых частых причин «тихих» ошибок в JavaScript-приложениях. Они не вызывают исключений, но могут приводить к непредсказуемому поведению, когда код выполняется не в том порядке или данные не загружаются. Линт-правило noFloatingPromises как раз призвано находить такие

Необработанные Promise — одна из самых частых причин «тихих» ошибок в JavaScript-приложениях. Они не вызывают исключений, но могут приводить к непредсказуемому поведению, когда код выполняется не в том порядке или данные не загружаются. Линт-правило noFloatingPromises как раз призвано находить такие ситуации на этапе статического анализа. Недавно Vercel и команда Biome объединили усилия, чтобы сделать это правило ещё более чувствительным к тонким пограничным случаям. Когда Biome подготовил раннюю версию обновления, они обратились к Vercel с просьбой помочь протестировать его на реальных, сложных примерах. В Vercel к этому подошли творчески: устроили внутреннее соревнование, кто придумает самый коварный пример кода, который всё ещё будет уязвим для «плавающего» Promise.
Как работает noFloatingPromises и почему это важно
Понятие «плавающего» Promise (floating Promise) возникает, когда Promise создаётся, но не возвращается и не обрабатывается через await или .then(). Такой Promise «висит в воздухе»: он выполняется, но результат его выполнения никто не ждёт. В случае ошибки она может остаться незамеченной, что особенно критично в серверных приложениях или при работе с асинхронными событиями.
Правило noFloatingPromises в Biome анализирует синтаксическое дерево и выявляет места, где Promise не используется должным образом. Однако стандартная реализация может пропускать некоторые паттерны, например, когда Promise передаётся в функцию или возвращается из тернарного оператора. Стресс-тестирование от Vercel помогло расширить покрытие таких случаев.
Какие примеры оказались самыми сложными?
В ходе соревнования инженеры Vercel придумывали примеры, где Promise «маскируется» под обычное выражение. Один из таких случаев — Promise, переданный в качестве аргумента функции, но не обработанный внутри неё. Другой — Promise, возвращённый из колбэка в методе массива (например, map или forEach). Третий — Promise, созданный в условном операторе и не присвоенный переменной.
Особую сложность составили примеры с цепочками вызовов: когда Promise обёрнут в другой Promise или используется с операторами spread. В таких случаях статический анализатор должен отследить поток данных через несколько уровней вложенности. Biome после обновления стал распознавать эти паттерны, что снижает риск пропущенных ошибок.
Технические детали: как Vercel и Biome проводили тестирование
Стресс-тестирование проходило в виде внутреннего хакатона. Участники писали фрагменты кода, которые, по их мнению, могли «обмануть» правило. Затем эти примеры запускались через линтер, и команда анализировала, какие из них не были обнаружены. На основе этих данных вносились правки в логику анализа.
Такой подход позволил выявить не только очевидные уязвимости, но и редкие сценарии, которые сложно предусмотреть при проектировании правила. Например, случай, когда Promise создаётся внутри функции-конструктора или геттера. В итоге Biome расширил набор проверяемых паттернов, что делает правило более надёжным для больших кодовых баз.
Какие ещё паттерны теперь обнаруживает правило?
Помимо упомянутых, обновлённое правило noFloatingPromises теперь ловит Promise, возвращаемые из стрелочных функций без тела, Promise в выражениях с запятой, а также Promise, переданные в деструктурирующее присваивание. Это особенно важно для проектов, где активно используются функциональные конструкции и цепочки вызовов. Разработчики, которые пишут на TypeScript с строгими настройками, теперь могут быть уверены, что линтер не пропустит даже самый хитрый «плавающий» Promise.
Кого затронет это обновление
В первую очередь обновление noFloatingPromises оценят разработчики, использующие Biome для линтинга TypeScript и JavaScript. Особенно это актуально для проектов с высокой асинхронной нагрузкой: серверы Node.js, приложения на React с асинхронными запросами, скрипты автоматизации. Для российских разработчиков, которые активно переходят на Biome как альтернативу ESLint, это улучшение повысит качество кода и снизит количество runtime-ошибок.
Кроме того, сам подход к стресс-тестированию может стать примером для других команд, разрабатывающих инструменты статического анализа. Вместо формального тестирования на синтетических примерах, Vercel и Biome применили краудсорсинг внутри компании, что дало более реалистичные кейсы.
Что дальше
Biome продолжит развивать noFloatingPromises и другие правила на основе обратной связи от сообщества. Ожидается, что новая версия войдёт в ближайший релиз Biome. Vercel, в свою очередь, планирует применять аналогичный подход к тестированию и других линт-правил, чтобы повышать качество инструментов, используемых в своей экосистеме.
Итог
Совместный стресс-тест Vercel и Biome показал, что даже зрелые линт-правила можно улучшить, если подойти к тестированию творчески. Обновлённое правило noFloatingPromises станет более надёжным инструментом для поиска необработанных Promise, что напрямую повысит стабильность JavaScript-приложений. Разработчикам стоит обновить Biome до последней версии, чтобы воспользоваться этими улучшениями.