Антон Тришин

Frontend-разработчик в Домклик

Привет, Хабр!

Среди разработчиков давно не утихает спор: действительно ли алгоритмы нужны в повседневной разработке или это лишь сухая теория для прохождения собеседований? Вот и я решил вступить в этот спор.

Когда я начинал карьеру во фронтенд-разработке, то постоянно задавался вопросами: «Зачем на интервью просят обходить граф, если на работе я просто правлю CSS-отступы?» или «Зачем знать сложность O(N), если вёрстка всё равно ломается в Safari?» С развитием искусственного интеллекта (ИИ) подобные вопросы стали звучать ещё громче: «Зачем мне вообще думать, если агент напишет этот цикл за меня?»

Однако по мере прокачки в алгоритмических задачах я начал видеть их применение в реальной разработке — там, где задача сводилась к чему-то более сложному, чем выравнивание div по вертикали. Я научился проектировать более лаконичные реализации бизнес-фич, быстрее находить узкие места в производительности и, самое главное, обрёл уверенность в собственных действиях.

Важный нюанс: да, можно составить качественный промт и получить оптимальный код от ИИ за секунды. Расходимся. Но ответственность за фичу всё равно лежит на разработчике, и баги исправлять придётся именно ему. Но как исправить баг, не понимая, как работает функция? Можно скормить нейросети ещё один промт и надеяться на чудо, но этот процесс рискует превратиться в бесконечное хождение по кругу. LLM отлично пишет код, но зачастую не знает ограничений именно вашего проекта. Она не понимает, сколько пользователей одновременно работает с системой, какие требования к памяти или где узкое место. Чем лучше разработчик понимает алгоритмы в основе фичи, тем точнее он может поставить задачу модели и тем быстрее отличит эффективное решение от красивого, но неподходящего.

Если вы чётко понимаете логику алгоритма, лежащего в основе фичи, то без труда сможете доработать и усовершенствовать решение или направить ту же LLM в нужное русло гораздо быстрее.

На основе своих наблюдений я пришёл к выводу, что алгоритмы — это не самоцель и не просто абстрактные задачи с LeetCode. Это фундамент, который даёт фронтендеру три ключевых преимущества:

  • Ускорение разработки за счёт использования готовых, проверенных временем и узнаваемых паттернов

  • Качество кода и принятие более зрелых инженерных решений при проектировании архитектуры компонентов

  • Прямое влияние на бизнес через оптимизацию производительности, стабильность интерфейса и пользовательский опыт (UX)

Ниже — несколько примеров того, как классические алгоритмические идеи проявляются в повседневной фронтенд-разработке. К каждому примеру я добавил ссылку на похожую задачу с LeetCode, чтобы было проще проследить связь между учебной задачей и её практическим применением.

Two Sum: быстрый доступ к данным

Представьте интернет-магазин: покупатель добавляет товар в корзину, а система должна понять, есть ли уже такой товар у пользователя, чтобы обновить количество или создать новую позицию. Наивный подход предполагает перебор всех элементов корзины при каждом добавлении. Пока товаров немного, это не вызывает проблем, но с ростом корзины производительность ухудшается.

В реальных системах подобную логику обычно реализуют через структуру данных типа ключ-значение, где ключом выступает productId. Это позволяет быстро проверять наличие товара и обновлять его количество, избегая обхода всей коллекции:

if (cart.has(productId)) { 
  cart.get(productId).count++;
} else { 
  cart.set(productId, { count: 1 });
}

На первый взгляд, это выглядит как обычная операция при работе с коллекцией в памяти. На практике сама идея чаще всего скрыта за базой данных, индексами или кеш-слоем, где проверка существования и обновление выполняются через быстрый доступ по ключу. Но и на клиенте логика аналогична — например, при структурировании данных в стейт-менеджере.

Именно этот принцип раскрывает задача LeetCode №1: вместо линейного перебора коллекции используется хеш-структура для мгновенного доступа к уже встреченным элементам. В одном случае мы ищем пару чисел с заданной суммой, в другом — управляем состоянием корзины. Контекст разный, но базовый алгоритмический паттерн один: замена полного перебора на доступ по ключу.

LRU Cache: кеширование данных

Представим обычное веб-приложение: при открытии каждой страницы оно запрашивает конфигурацию — feature-флаги, настройки интерфейса, списки доступных языков и валют и другие справочные данные. Пока пользователь перемещается по приложению, эта конфигурация практически не меняется, но без кеширования фронтенд продолжает отправлять одни и те же запросы. В результате растёт нагрузка на API, увеличивается количество сетевых запросов, замедляется SSR, а вместе с ним ухудшаются и Web Vitals.

Решение — кеш с вытеснением давно неиспользуемых значений:

const key = "/api/config?locale=ru&currency=RUB"; 

let config = cache.get(key); 

if (config === -1) { 
  config = await api.get(key); 
  cache.put(key, config); 
}

return config;

Снаружи — обычное кеширование, а внутри cache.get() и cache.put() скрывается та самая логика LRU Cache из задачи LeetCode №146: при каждом обращении запись помечается как «недавно использованная», а при переполнении кеша система автоматически удаляет ту, к которой давно никто не обращался. Такие алгоритмы ежедневно работают в реальных системах и напрямую влияют на скорость и стабильность приложений.

Merge Intervals: работа с диапазонами

Здесь рассмотрим систему бронирования переговорных комнат. Фронтенд получает список занятых интервалов и должен отобразить их в виде таймлайна в календаре. Часто данные пересекаются или идут встык (например, после синхронизации из нескольких источников). Чтобы вместо набора разрозненных блоков система построила непрерывное расписание, данные нужно нормализовать.

До обработки:

09:00–10:00
09:30–11:00
11:00–12:00
13:00–14:00
13:30–15:00
16:00–17:00

После:

09:00–12:00
13:00–15:00
16:00–17:00

Обычно эта задача выполняется на стороне бэкенда или базы данных, но в некоторых случаях — например, при локальных изменениях или объединении источников — её могут выполнять и на клиенте. Именно такую задачу решает LeetCode №56 — Merge Intervals, позволяя объединить пересекающиеся интервалы в минимальный набор диапазонов.

Этот алгоритм регулярно используют при создании календарей, временных шкал, диаграмм занятости и любых интерфейсов, работающих с временными интервалами.

Promise Time Limit: контроль таймаутов API

Допустим, есть веб-приложение, которое при загрузке запрашивает данные из нескольких внешних источников. Обычно API отвечает за 100—200 мс, но иногда из‑за сетевых сбоев или высокой нагрузки запрос может «зависнуть» на десятки секунд.

Если просто ждать выполнения Promise, пользователь будет бесконечно смотреть на индикатор загрузки, а SSR может существенно замедлиться. Поэтому в реальных проектах запросы почти всегда ограничивают по времени:

const data = await timeLimit( 
  () => api.get("/api/profile"), 
  3000 
);

Если API не ответил за условные три секунды, запрос прерывается, а приложение выводит сообщение об ошибке или переключается на fallback.

Задача на LeetCode №2637 формализует эту ситуацию: выполнение асинхронной операции с жёстким ограничением по времени. Подобный механизм ежедневно используют в продакшене при работе с внешними API, микросервисами и любыми потенциально долгими асинхронными операциями.

Формально Promise Time Limit сложно назвать классическим алгоритмом вроде бинарного поиска. Скорее, это инженерный паттерн управления асинхронными операциями. Тем не менее подобные задачи развивают критически важный навык: умение ограничивать время выполнения операций, корректно обрабатывать исключения и проектировать отказоустойчивые системы.

Top K Frequent: популярные данные

В интернет‑магазинах почти всегда есть разделы вроде «Популярные товары», «Чаще всего покупают» или «Рекомендуем». В большинстве крупных систем эта логика уже реализована на уровне базы данных или аналитического слоя через агрегации, индексы или специализированные системы аналитики. Однако если возникает необходимость реализовать такую механику самостоятельно, в основу ложится задача подсчёта частот: из большого потока событий нужно выбрать K самых популярных элементов. Именно этот паттерн формализует LeetCode №347.

Конечно, это далеко не полный список. Во фронтенд-разработке можно найти множество других примеров применения алгоритмических идей. Например, бинарный поиск используют внутри библиотек виртуализации списков, таких как react-virtualized и TanStack Virtual, позволяя быстро определять, какие элементы нужно отрисовать при прокрутке.

Понимание алгоритмических принципов позволяет не просто использовать готовые инструменты, а проектировать собственные решения, учитывающие особенности и требования конкретного продукта.

Итог

На мой взгляд, алгоритмы нужны не для того, чтобы разводить бесконечные споры или штурмовать рейтинги на LeetCode. Их реальная ценность в другом — в понимании того, какие подходы вообще существуют и как они поведут себя в живой системе.

Это напрямую влияет на продукт: позволяет быстрее находить масштабируемые решения вместо «простых, но дорогих» костылей и даёт глубокое понимание того, что происходит под капотом привычных инструментов — от баз данных и систем кеширования до современных фреймворков.

Чем лучше разработчик распознаёт эти паттерны, тем чаще он создаёт не просто работающий код, а архитектуру, которая остаётся устойчивой при росте нагрузок, объёмов данных и общей сложности системы.

Комментарии (8)


  1. hardtop
    16.07.2026 08:40

    Хорошие примеры со ссылками на релевантные кодовые задачки!

    Учить надо, просто так много стало информации. Во фронте и дизайн надо понимать и уметь, и верстку, и тексты-картинки-смысл. И всякие сео-гео. А в сутках так и осталось 24 часа


    1. aatrishin Автор
      16.07.2026 08:40

      спасибо! Наверное, главный секрет, как во всем этом не выгореть - относиться ко всему с искренним интересом!


  1. Programist18946
    16.07.2026 08:40

    Понравилась мысль, что LeetCode это не самоцель, а способ научиться видеть паттерны, которые потом встречаются в реальных задачах. С приходом LLM это стало даже актуальнее.