Код пишет нейросеть. Что остается разработчику?
Привет, Хабр!
В последнее время я часто слышу апокалиптические прогнозы: «Скоро программисты будут не нужны, Claude напишет любой сервис за секунду». Я больше 10 лет руковожу студией разработки, и мой опыт применения нейросетей на практике в наших проектах сегодня говорит об обратном: программисты никуда не исчезнут, но их роль меняется прямо сейчас.
Я различаю два принципиально разных подхода к использованию LLM в разработке:
-
Первый — условный «вайб-кодинг»: вы кидаете в чат задачу и ждёте, что нейросеть как-то сама придумает архитектуру, сценарии и интерфейсы.
-
Второй — частичная автоматизация: разработчик остаётся режиссёром процесса (анализ, проектирование, архитектура, код-ревью), а нейросеть берёт на себя рутину — генерацию кода и документации. Такой подход с работой LLM на основе спецификации ещё называют Spec-Driven Development (SDD).
Второй подход к разработке с применением нейросетей мне напоминает костюм Железного человека. Он не заменяет Тони Старка, но даёт ему суперсилу: летать, стрелять и вычислять траектории за доли секунды. Без костюма Старк — всё еще гениальный инженер, но с костюмом — супергерой, способный в одиночку остановить армию. Так и SDD превращает квалифицированного разработчика в "армию из одного человека", кратно усиливая его возможности.
Безусловно, хорошие инженеры всегда начинали работу с вопросов «Что?» и «Зачем?». Но даже после глубокого анализа львиная доля времени всё равно уходила на низкоуровневое «Как?»: на борьбу с синтаксисом, написание бойлерплейта и ручную реализацию стандартных паттернов.
Сегодня этот слой работы — написание кода — схлопывается до 5-10%. Нейросети отлично справляются с реализацией, освобождая нас для высокоуровневого «Как?»: проектирования архитектуры и выстраивания связей.
Теперь наша работа — это не укладка кирпичей, а создание чертежей.
Я хочу показать этот сдвиг на живом примере из нашей практики. Этот кейс позволил нам на деле убедиться, какие компетенции и навыки действительно решают при разработке с LLM. Мы увидели, как неверный подход к постановке задачи и отсутствие подготовительного этапа бизнес-анализа чуть не привели нас к созданию бесполезного продукта, а смена парадигмы позволила решить задачу за пару часов.
Ловушка «очевидной» задачи
Вводные: мы развиваем e-commerce мобильное приложение для нашего клиента, используем Firebase для push-рассылок по всем пользователям, но маркетинг захотел персонализацию — отправлять пуши о смене статуса заказа, о брошенной корзине или снижении цены на избранное.
Дальше всё развивалось по очень узнаваемому сценарию. Менеджер обсудил с клиентом боли и принёс в разработку задачу с на первый взгляд понятной формулировкой:
«Подключить Firebase SDK для управления push-уведомлениями» — с перечнём кейсов, когда нужны пуши.
Для команды это выглядело как идеальный кандидат для того, чтобы сделать с нейросетями: функционал не ключевой, риски низкие, есть официальная SDK с понятным API.
В этот момент мы сделали то, что делают многие: отнеслись к модели как к «умному разработчику». Раз в тикете написано «управление push-уведомлениями», значит, это какая-то админка — мы просто переслали формулировку задачи в чат с LLM почти как есть:
«Реализовать приложение на Node.js, которое будет предоставлять интерфейс пользователю для управления Push-уведомлениями через Firebase. Для реализации использовать официальную библиотеку Firebase Admin SDK для работы с Firebase».
Модель послушно сгенерировала админку: формочки, кнопочки, списки пользователей. Технически всё выглядело аккуратно, но по смыслу — мимо задачи.
В этот момент стало очевидно, что проблема не в ИИ, а в исходной постановке: мы сфокусировались на интерфейсе «управления», хотя бизнесу нужна была автоматическая реакция на события. Никто не будет сидеть и вручную слать тысячи пушей пользователям с брошенными корзинами — нужна была автоматизация, а не инструмент ручного труда.
Мы остановились, сделали шаг назад и переформулировали задачу так, чтобы она отражала реальный бизнес-процесс. Дальше я расскажу по шагам, какие именно этапы нам понадобились и как вообще стоит подходить к задачам программирования с использованием LLM, чтобы не попасть в ловушку «очевидной» задачи.
Этап 1: Поиск смысла
Когда задача только поступает от бизнеса (или рождается в вашей голове), отправлять её нейросети в разработку — бесполезно. LLM по умолчанию не знает контекста вашего проекта, боли ваших клиентов и ограничений вашего легаси.
На этом этапе ваша задача — интерпретировать и декомпозировать.
В нашем случае это выглядело как серия вопросов "Зачем?" и "Откуда?":
— Зачем нам админка?
— Чтобы отправлять пуши Васе, который не оплатил шпаклёвку из корзины в приложении.— Кто будет нажимать кнопку "Отправить"?
— Никто, это должно работать само.
— Откуда мы узнаем, что Вася забыл шпаклёвку?
— Бэкенд интернет-магазина пришлет событие.
Так мы поняли, что "админка" — это ложная цель, фантом, возникший из-за привычки мыслить интерфейсами. Истинная цель — автоматическая реакция на события.
Мы декомпозировали процесс на триггеры (корзина брошена, статус изменен) и действия (отправить пуш). Это довольно простая, но крайне важная аналитика: отсечь лишнее (UI) и кристаллизовать суть (вебхук). ИИ здесь не помощник, потому что если бы мы спросили его "как сделать админку", он бы просто сделал админку, не задавая вопросов "а зачем?".
Этап 2: Определение требований и архитектуры
После такой декомпозиции техническая формулировка задачи стала гораздо конкретнее. По сути, нам нужен один сервис, который:
-
Принимает от сайта
webhookс данными: кто пользователь (deviceIdили списокdeviceId), какой сценарий / текст сообщения; -
Через
Firebase Admin SDKотправляет push-уведомление в мобильное приложение; -
Возвращает ответ Firebase, чтобы сайт мог помечать недоставленные токены и не тратить ресурсы впустую.
У Firebase самая полная реализация Admin SDK на Node.js, а лезть в легаси-PHP с самописной библиотекой не хотелось. Поэтому техническая цель сузилась до простой формулировки: отдельный микросервис на Node.js, который выступает webhook-шлюзом между сайтом и Firebase Cloud Messaging.
На этом этапе пора обратиться к LLM — но не за «кодом всего сервиса», а как к архитектурному консультанту.
Формулируем высокоуровневый запрос примерно следующего содержания:
«Мне нужен backend на Node.js, который будет служить webhook для инициации рассылок в мобильные приложения через Firebase Cloud Messaging на базе Admin SDK. Какую архитектуру ты предлагаешь, что нужно учесть? Задай вопросы, если необходимо».
Дальше диалог с моделью шел по нескольким линиям:
-
Про общую схему: HTTP-сервер (Express/Fastify), валидация входящих данных, возможность вынести отправку в отдельный воркер/очередь, чтобы не душить сервер при всплесках;
-
Про особенности Firebase Cloud Messaging: лимиты, batch-отправка, что сейчас считается best practice;
-
Про работу с токенами устройств: что делать с протухшими device-токенами и как не тратить ресурсы на бессмысленные отправки.
Здесь особенно важен доступ к актуальной документации, поэтому используем инструменты с поиском (Perplexity, ChatGPT или Grok), а не «голую» модель без интернета. Типовые вопросы выглядели так:
-
«Какие сейчас есть современные способы валидации JSON в Node.js для хайлоада? Zod vs TypeBox?»
-
«Какие лимиты у Firebase FCM на массовые и индивидуальные рассылки?»
-
«Как правильно обрабатывать невалидные device-токены в Firebase Admin SDK?»
В результате из этого диалога сложился осознанный набор решений:
-
Admin SDK на Node.js как основной способ общения с Firebase;
-
Fastify в качестве HTTP-сервера;
-
TypeBox для описания и валидации JSON-схем прямо внутри Fastify;
-
Отказ от тяжелой message-queue (RabbitMQ и т.п.) на первом этапе, потому что сценарии у нас индивидуальные, а не массовые кампании;
-
Использование
sendEachForMulticastвместо устаревшегоsendMulticastи обязательная обработка ответа Firebase с пометкой невалидных токенов в базе сайта.
К этому моменту у нас появился достаточно детализированный «скелет» архитектуры, который уже можно было превратить в формальную спецификацию для генерации кода.
Этап 3: Сборка спецификации
Теперь у нас есть понимание что делать и как делать: есть webhook-сервис, выбран стек, понятны ограничения. Следующий шаг — собрать всё это в спецификацию, которую мы передадим непосредственно LLM для генерации кода.
По сути здесь начинается Spec-Driven Development: мы сначала фиксируем в документе структуру сервиса, контракты, сценарии и ограничения, а уже потом даём этот документ LLM как источник правды. Модель работает не «с наскока» по одной размытой фразе в чате, а по подготовленному плану.
Вот как выглядел наш итоговый промпт для генерации спецификации (сравните с тем, что был в начале):
я проектирую бекенд приложения, у которого будет один webhook-endpoint который будет принимать на входе информацию для отправки push-уведомлений через firebase (тип сообщения - notification).
бек должен подключаться к firebase через официальный node.js firebase sdk.
стек - fastify, typebox.
защита endpoint через bearer auth.
должна быть поддержка Batching отправки (sendEachForMulticast).
webhook должен возвращать ответ от firebase, чтобы потом я мог его использовать.
покрой код unit-тестами.
напиши md с проектированием приложения. задай вопросы если необходимо.
И даже здесь мы добавляем страховку: инструкцию использовать MCP (Model Context Protocol) — стандарт подключения внешних данных к LLM. С его помощью мы даём модели доступ к актуальной документации через Context7, чтобы избежать галлюцинаций с устаревшими методами.
Лайфхак: давайте промпт на генерацию спецификации нескольким разным LLM, сравнивайте качество выполнения и оставляйте лучшие решения. Также полезно разные модели привлекать для ревью файлов с документацией, написанные другими LLM.
Этап 4: Производство и тестирование
И только теперь, когда у нас есть выверенное ТЗ, мы приступаем к коду. В таком объёме, как в нашем примере, технически можно попросить LLM сгенерировать весь сервис одним запросом — и нередко он даже будет работать.
Но надёжнее и управляемее вести работу итеративно, модуль за модулем, где scope каждого шага определяет человек-архитектор. Типичный план для такого сервиса может выглядеть так:
-
«Реализуй базовый сервер Fastify с подключением Firebase по этой спецификации».
-
«Добавь валидацию Payload через TypeBox согласно схеме».
-
«Реализуй логику батчинга в сервисе отправки».
-
«Напиши unit-тесты для сервиса отправки».
В таком режиме ИИ выступает как идеальный Junior+/Middle, который пишет код строго по гайдлайнам, не отвлекаясь на самодеятельность. Задача разработчика — ревьюить каждый чанк и интегрировать его в общую картину. Это позволяет держать контроль над кодовой базой и избегать "спагетти-кода", который так любят генерировать нейросети, если их не ограничивать.
Итого: Алгоритм работы программиста в эпоху LLM
Если резюмировать наш опыт, цикл разработки теперь выглядит так — на примере мы прошли по этим шагам от начала до конца:
-
Понимание и Декомпозиция (Человек): Человек формулирует понимание задачи на уровне бизнес-логики, разбивает систему на компоненты, подготавливает контекст для ИИ.
-
Архитектура (Человек + ИИ-копилот и рисчерчер): Выбор стека и проектирование связей с учетом современных практик: ИИ как консультант и поисковик.
-
Спецификация (Человек + ИИ-технический писатель): Человек пишет подробный промпт с техническими требованиями и ограничениями; ИИ генерирует детализированную спецификацию, которую человек ревьюит и уточняет.
-
Реализация (ИИ-генератор кода + Человек-код ревьювер): Итеративная генерация кода по спецификации с обязательным ревью каждого шага; прохождение интеграционных и unit-тестов.
-
Верификация (Человек): Ревью кода и проверка бизнес-логики.
Вот репозиторий проекта с кодом, который написал ИИ в нашем кейсе – там же есть три разных документа со спецификациями, написанные разными LLM в процессе проектирования.
Важно понимать: этот метод — полная противоположность «вайб-кодингу», когда вы бросаете в LLM расплывчатую задачу и ждёте чуда. Вайб-кодинг даёт красивые, но часто бесполезные решения без контроля за техническим стеком, архитектурой и бизнес-логикой.
Подход, о котором я пишу, — это не безконтрольная «магия», а частичная автоматизация процесса разработки: LLM встроена в конкретные шаги процесса, а ответственность за анализ, архитектуру и контроль результата остаётся у разработчика. Это не "волшебная таблетка", которая позволит уволить всех программистов, а инструмент, который кратно усиливает сильного инженера. Но если у вас нет архитектурного мышления, нейросети лишь помогут вам быстрее создать неработающего монстра.
Какие навыки нужны разработчику будущего?
Я выделяю для себя список компетенций, которые становятся must-have, когда синтаксис за вас пишет машина:
-
Системный анализ. Умение извлекать из бизнес-заказчика требования и переводить абстрактные боли в конкретные технические сценарии. Если вы сами не понимаете цели, ИИ додумает за вас, и скорее всего – неверно, если вы не предоставите достаточно контекста.
-
Архитектурная насмотренность. Вы должны знать паттерны, лучшие практики и антипаттерны. Если вы не знаете, что такое микросервисы или идемпотентность, вы не сможете попросить ИИ реализовать их в коде так, как вам требуется. Вы будете принимать любой работающий код, даже если архитектурно это бомба замедленного действия.
-
Навык декомпозиции. Способность разбить большую задачу на серию атомарных этапов. Это действенный предохранитель от галлюцинаций нейросетей: чем более размытую и неопределённую задачу вы дадите ИИ, тем больше шансов на её неточную интерпретацию, что приведет к ошибкам в реализации.
-
Code Review на стероидах. Теперь вы читаете кода и спецификаций в 10 раз больше, чем пишете. Навык быстро сканировать глазами сгенерированный код и документацию и находить в них логические дыры (а не пропущенные запятые) становится критическим.
-
Context Engineering (Управление контекстом LLM). Нейросеть — идеальный исполнитель без собственного мнения. Если вы не дадите ей ваши корпоративные стандарты, стайл-гайды и «манифесты», она напишет код так, как «видела в интернете», а не так, как принято у вас. Умение собрать правильный «пакет документов» перед началом генерации — это то, что отличает production-ready код от поделки.
В эпоху LLM ваша ценность как программиста не в том, насколько быстро вы пишете код, а в том, насколько точно вы понимаете, какой код вообще нужно написать. Разработчик превращается из "писателя" в "режиссера", который определяет замысел, разбивает его на сцены и контролирует каждый дубль. А код? Код — это просто расходный материал.
Именно это отличает инженера, который использует частичную автоматизацию с LLM и spec-driven подход, от «вайб-кодера», который перекладывает ответственность за смысл и архитектуру на модель.
Поделитесь мнением, как вы думаете, какой будет профессия программиста в будущем и как она трансформируется прямо сейчас — здесь в комментариях или в моем Telegram-канале, где я тоже рассказываю о том, как нейросети меняют процесс разработки цифровых продуктов и делюсь практическими примерами.