Вайб-кодинг или осознанная разработка? Я выбираю второе

от автора

С приходом ИИ сильно вырос соблазн заниматься простым человеческим вайб-кодингом: пишешь себе промпты, копируешь готовый код, вставляешь в проект — и готово! И бизнес рад, и времени меньше уходит… В чем подвох? Пожалуй, в том, что таким образом все меньше полезной информации оседает в голове.
В этой статье я хочу поделиться своим методом, с помощью которого я внедряю новые фичи в проект, над которым работаю, при этом получая новые знания и опыт (как в старые добрые времена), затрачивая гораздо меньше времени.
В настоящее время я занимаюсь разработкой фронтенда в над starpx.com — это приложение, позволяющее просматривать и редактировать Deepsky-фотографии, поэтому для статьи взял оттуда пару личных кейсов.
Возможно, описываемые в статье технологии покажутся кому-то «детскими» и простыми, но главное — это принцип взаимодействия с ними человека, который про них только слышал, но не использовал.

TL;DR

Мой процесс внедрения новых (в первую очередь — для меня) фичей выглядит так:

  1. Тренируй насмотренность

  2. Изучи с чем имеешь дело

  3. Спроси ИИ и проанализируй ответ

  4. Внедряй

1. Тренируй насмотренность

Во все времена одним из признаков хорошего специалиста является насмотренность. Быть в тренде, читать про новые технологии — это как правило хорошего тона любого инженера. Ибо если ты сам себе землекоп с лопатой, то, не прочитав про экскаватор, ты не будешь его применять.
Для ознакомления с тем, “чем живет” твоя предметная область, не нужно много времени и сил, достаточно посмотреть доклад с конференции или почитать статью вместо листания ленты в кофе-брейк — чтобы хотя бы было представление.
И вот тебе прилетает новая задача, при виде которой обычный человек скорее всего пойдет гуглить на пару часов, далее пару дней обдумывания, дейлики, созвоны, асинхронные консультации и тд и тп. Но не в нашем случае — у нас есть насмотренность.

Личный кейс № 1: тайлинг

Задача: сделать разделение изображения на фрагменты небольшого разрешения (тайлы), которые при размещении рядом друг с другом составляют само изображение. При этом, должно поддерживаться неограниченное количество уровней тайлинга, фрагменты должны подгружаться по мере увеличения степени зумирования, а итоговое изображение должно собираться в SVG (на этом срезалось пару библиотек, которые строят изображение в canvas).

Участок звездного неба, обработанный в StarPX

Участок звездного неба, обработанный в StarPX

Джуном я бы побежал решать задачу «в лоб», нагородил бы условий и кастомных событий и, скорее всего забыл бы про оптимизацию.
Теперь, в более зрелом возрасте и имея насмотренность, я могу добавить к условиям оптимизацию: при увеличении или перемещении области просмотра должны подгружаться только те тайлы, которые находятся на экране в данный момент. Как это отследить? Ну, я слышал про такую штуку, как IntersectionObserver, но ни разу ее не применял. Было бы круто поработать с ней!

Личный кейс № 2: диаграммы

Вряд ли человек, не увлекающийся астрономией или астрофотографией, знает, что звезды имеют свои характеристики и даже диаграммы, их описывающие. Так, диаграмма Герцшпрунга-Рассела отражает зависимость между спектральными классами или показателями цветовой температуры звёзд и их абсолютными звёздными величинами, а диаграмма светимости показывает зависимость силы излучения для определенных частот световых волн. Разумеется, уважающий себя сервис должен уметь отображать эти диаграммы.
Учитывая, что в проекте для отрисовки overlay-слоев уже использовалась библиотека D3.js, глупо было бы использовать еще одну отдельно для графиков.

Диаграмма Герцшпрунга - Рассела

Диаграмма Герцшпрунга — Рассела

Личный кейс № 3: ускорить загрузку информации об объектах звездного неба

При открытии пост-обработанного изображения у нас есть overlay-фигуры, размечающие различные космические объекты: галактики, туманности, звезды и т.д. Разумеется, разметить абсолютно все объекты даже на отдельном участке звездного неба невозможно — будет каша. Но самые значительные видны и подписаны. Мало того: при нажатии на них можно посмотреть более подробную информацию, в том числе диаграммы.
Пожалуй, любому придет в голову идея с кешированием. И, не имея насмотренности, я бы скорее всего побежал бы костылить сохранение этой информации в localStorage.
Под действием алкоголя насмотренности же, я вспомнил про такие классные штуки, как:

  1. IndexedDB — идеально, чтобы сохранить информацию об объектах звездного неба в табличном виде.

  2. Worker’ы — чтобы делать это в отдельном потоке, невидимо для пользователя.

Объекты в IndexedDB

Объекты в IndexedDB

Сразу в голове звучит: “О! Так я же читал про эту библиотеку/API на прошлой неделе, можно применить!” Ну и продолжение — “…вобью-ка в промпт…” В редких случаях это сработает, но лучше…

2. Изучи с чем имеешь дело

Гораздо более эффективным будет сначала изучить технологию, которую собираешься использовать, на достаточном уровне, чтобы а) можно было сделать все самому — чтобы в голове отложилось б) поправить код ИИ — чтобы не тратить время на бесконечные копипасты говнокода, который в лучшем случае работать не будет, а в худшем будет немасштабируемым и неотлаживаемым. Найдите туториал, посмотрите рабочие примеры, пройдите мини-курс. Наша задача — изучить на ДОСТАТОЧНОМ уровне, не погружаясь в детали на несколько недель. Здесь уместно вспомнить про принцип Парето (80/20).
Для первого кейса с тайлингом, который фактически не зависел от других библиотек моего проекта, я сделал мини-полигон в виде большого VueJS компонента, единственного отображаемого на странице. Остальные так или иначе использовали подключенные библиотеки приложения.
В изучении IntersectionObserver мне хорошо помог проект doka.guide — я немного потренировался, понял, что к чему, и «зачел» себе это знание.
Ознакомление с библиотекой D3.js проходил с использованием руководства здесь — я не большой фанат обучающих видео, мне ближе классические учебники с примерами.
По IndexedDB и Worker API я прошелся по официальной документации и статьям на хабре (благодарю всех авторов, коих я, к сожалению, не запомнил).
После того, как я потрогал все нужные технологии руками и убедился, что могу все сделать сам, я…не стал все делать сам, а пошел к нейросетям.

3. Спроси ИИ и проанализируй ответ

Как только чувствуешь, что можешь сделать самостоятельно, но при этом ясно представляешь, что это будет долго и нудно — можешь подключать ИИ-ассистента. Скорее всего, он быстро накидает “скелет” того, что тебе нужно, а благодаря своим знаниям, приобретенным на предыдущем шаге, ты легко сможешь переделать сгенерированный шаблон в рабочий код, который станет частью твоего проекта. Если есть возможность не вставлять код сразу в проект — лучше этого не делать. Вместо этого, сделать полностью рабочий прототип, возможно используя отдельное окружение. В целом код, сгенерированный ИИ, похож на полуфабрикат, типа пельменей: вряд ли их можно съесть, не сварив. Так же с кодом: не работает — “вари”, работает — внедряй.
К сожалению, данную статью я решил написать уже после успешного внедрения описываемых решений, поэтому прикрепить код, выданный LLM, не могу — он ушел в историю. Могу сказать только, что с моими задачами на удивление неплохо справлялся GigaChat от Сбера — и это очень приятный момент, что отечественные разработки становятся по-настоящему конкурентоспособными.

4. Внедряй

На последнем этапе переходим к внедрению уже готового кода в наш проект. Здесь мы подключаем зависимости, переводим имена переменных в Namespace проекта (в идеале это сделать уже в п. 3), пишем необходимые экспорты и импорты.

В кейсе с тайлингом я получил почти сразу рабочую обертку IntersectionObserver для тайлов, которые были на экране. Все, что мне оставалось, это проверить работу моего компонента и просто перенести его в проект.
Код диаграмм работал, но не совсем корректно — были проблемы с масштабом диаграммы и с использованием неподдерживаемых методов библиотеки D3.js, которые были заменены в новой версии библиотеки. К счастью, я изучил библиотеку D3.js и легко смог поправить код, чтобы он работал так, как надо, и рисовал то, что требуется.
В работе с IndexedDB LLM выдал мне рабочий composable-костяк для работы с IndexedDB, в котором править пришлось разве что имена базы данных и хранилища:

export interface StarData {   id: string   info: any // тут секрет =) }  const DB_NAME = 'starDB'  const DB_VERSION = 1  const STORE_NAME = 'stars_info'  function openDB(): Promise {    return new Promise((resolve, reject) => {      const request = indexedDB.open(DB_NAME, DB_VERSION)      request. => reject(request.error)      request.onsuccess = () => {        resolve(request.result)      }      request.onupgradeneeded = () => {        const db = request.result        if (!db.objectStoreNames.contains(STORE_NAME)) {          db.createObjectStore(STORE_NAME, { keyPath: 'id' })        }      }    })  }   export async function getStarData(id: string): Promise {    const db = await openDB()    return new Promise((resolve, reject) => {      const tx = db.transaction(STORE_NAME, 'readonly')      const store = tx.objectStore(STORE_NAME)      const request = store.get(id)      request.onsuccess = () => resolve(request.result)      request. => reject(request.error)    })  }  export async function setStarData(data: StarData): Promise {    const db = await openDB()    return new Promise((resolve, reject) => {      const tx = db.transaction(STORE_NAME, 'readwrite')      const store = tx.objectStore(STORE_NAME)      const request = store.put(data)      request.onsuccess = () => resolve()      request. => reject(request.error)    })  }

Аналогичная ситуация с Worker’ом — почти с первого раза рабочий код, редактировал только обмен информацией между потоками и идентификацию объектов для записи в БД:

/// <reference lib="webworker" />  import { getStarData, setStarData, StarData } from './IndexedDB' import fastHash from '@/core/utils/FastHash'  const fetchAdditionalInfo = async (url: string) => {   const response = await fetch(url)   const info = await response.json()   return info }  interface Star {   id: string   // rest fields }  interface WorkerRequest {   stars: Star[] }  interface WorkerResponse {   id: string   // info?: any - здесь прилетает контент    ok?: boolean   error?: string }  self.addEventListener('message', async (event: MessageEvent<WorkerRequest>) => {   const { stars } = event.data   for (const star of stars) {     const id = fastHash(star.id)     try {       let starData = await getStarData(id)       if (!starData) {         const info = await fetchAdditionalInfo()         starData = { id, info }         await setStarData(starData)       }       const response: WorkerResponse = { id, ok: true }       self.postMessage(response)     } catch (error: any) {       const response: WorkerResponse = { id, ok: false, error: error.message }       self.postMessage(response)     }   } })

Выводы

В описанном подходе я вижу следующие плюсы:

  1. Вы приобретаете опыт и знания, а не просто копипастите ответы LLM к себе в проект

  2. Вы не тратите время на рутину — ее за вас делает ИИ, на вас — архитектура и внедрение

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

Конечно, можно (и даже нужно) добавить к перечисленным шагам этап ревью, но как минимум это лучше, чем бездумные Ctrl+C — Ctrl-V.

Спасибо, что дочитали, буду рад конструктивным комментариям!


ссылка на оригинал статьи https://habr.com/ru/articles/908590/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *