February 17, 2023

Улучшение опыта работы фронтенд-разработчика с помощью AI. Пишем свою обертку над GPT-3

Привет! Вы наверняка слышали множество разговоров о том, как AI изменит мир и нашу работу в будущем. Однако, многие из этих разговоров не концентрируются на том, как именно можно использовать технологию AI для улучшения конкретных процессов. В этой статье мы поговорим о том, как использование GPT-3 может помочь вам улучшить опыт работы frontend-разработчика.

GPT-3 — это мощный искусственный интеллект, который может генерировать тексты на основе анализа огромного количества данных. Он может быть очень полезен для различных задач, связанных с frontend-разработкой, от написания кода до создания документации.

Как GPT-3 может помочь фронтенд разработчикам

Давайте рассмотрим некоторые из задач, с которыми сталкиваются фронтенд-разработчики, и посмотрим, как GPT-3 может помочь в их решении:

  • Написание кода. Часто, чтобы написать хороший код, нужно учитывать множество факторов, например контекст, задачу, которую нужно решить, и возможные ошибки. С помощью GPT-3 вы можете генерировать умные подсказки, которые помогут вам написать более качественный код быстрее. Например, инструменты, основанные на GPT-3, могут генерировать код на основе входных данных и контекста, что может сэкономить много времени и усилий. Один из примеров такого инструмента — Github Copilot, который следит за контекстом написания и предлагает умные подсказки.
  • Тестирование. Создание тестов — важная часть работы frontend-разработчика, которая помогает убедиться, что код работает правильно и не содержит ошибок. GPT-3 может помочь сгенерировать базовые тесты автоматически на основе ваших данных, что позволит сэкономить время и силы. Также GPT-3 может использоваться для создания автоматических тестов, которые проверяют работу приложения в широком диапазоне сценариев. Я тестировал написание тестов с помощью ChatGPT и результат был отличным. Но в текущем рабочем проекте мы не пишем тесты, поэтому оставлю эту идею на следующую публикацию.
  • Документация. Создание документации — это важная, но не всегда приятная работа. GPT-3 может помочь автоматизировать процесс создания документации. Последние полгода я использую VSCode расширение Mintlify для автоматической генерации JSDoc документации. В Javascript это позволяет добиться небольшой типизации и упростить работу для других разработчиков. Более комплексные участки кода я по прежнему документирую вручную.

Developer Experience

Что скрывается за термином «Developer Experience»? Это может быть все, что угодно, начиная от языка программирования и фреймворков и заканчивая конвенциями о форматах API, линтерах, именах переменных, коммитах и т. д. В этой статье мы рассмотрим то, что я подразумеваю под этим термином.

Developer Experience означает, что разработчику не нужно думать о каких-то правилах, таких как кодстайлы, правила для линтеров и конвенции по написанию кода.

Недавно я просил ChatGPT исправить некоторые eslint-ошибки в проекте, и он прекрасно справился с этой задачей. Однако стоит ли создавать отдельный инструмент для этого, если уже есть автоматическое исправление в eslint? Я считаю, что нет. Лучше научиться настраивать eslint.

Однако, что действительно может вызывать проблемы — это коммиты. Для их именования существуют готовые конвенции, например, Conventional Commits. Для проверки этих коммитов можно использовать Commitlint.

Представьте, что вам нужно разобраться в том, что делают эти изменения кода. Где удобнее?

Я также являюсь большим поклонником использования Gitmoji в проектах. Эта методология позволяет гораздо проще интерпретировать тип изменений, вносимых в код, одним смайликом. Gitmoji — это отличный способ отслеживать изменения, которые вносятся, и это может сэкономить много времени при просмотре кода. В общем, использование Gitmoji — это невероятно полезный инструмент, который я очень рекомендую всем, кто работает с кодом в команде.

Атомарные коммиты — это когда каждый коммит задевает минимальную область изменений (обычно до 20-30 строк кода и одного файла)

Атомарные коммиты могут быть полезны для разработчика. Они помогают сделать ясное и легко читаемое представление изменений. Лучше делать мелкие коммиты, чтобы было проще отслеживать историю, откатывать изменения и исправлять ошибки.

Но вводить 100 символов описания для каждых 10 строчек — лень 🫡

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

Прогаем

Для начала нам понадобится аккаунт OpenAI. Если у вас еще нет аккаунта OpenAI – можете воспользоваться вот этим гайдом. Далее нам нужно получить OpenAI API KEY по этому адресу.

Так же, необходимо установить NodeJS, следуя оффициальному гайду.

Логика формирования запроса к нейронке

Сгенерируем prompt. Путем 10-15 экспериментов с ChatGPT, у меня получился примерно такой prompt, который генерирует осмысленные и подходящие по типу сообщения:

Generate a short commit title based on diff changes above, using gitmoji and conventional commits. Structure: <emoji> <type>: <subject>'

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

Так же, я хочу сделать скрипт универсальным и запускать его в любой директории (через глобальную установку, значит необходимо добавить глобальную обработкуOPENAI_API_KEY, чтобы его не пришлось указывать в самом файле. Поэтому добавим обработку из параметров запуска скрипта, а так же из глобального env файла.

const getArgs = () => {
  return process.argv.slice(2)
    .reduce((args, arg) => {
      if (arg.startsWith('--')) {
        const [flag, value = true] = arg.split('=');
        args[flag.slice(2)] = value;
      } else if (arg[0] === '-') {
        arg.slice(1).split('').forEach(flag => {
          args[flag] = true;
        });
      }

      return args;
    }, {});
};

const args = getArgs();

const apiKey = args.apiKey || process.env.OPENAI_API_KEY || (() => {
    error('Please set the OPENAI_API_KEY environment variable.');
    process.exit(1);
})();

Проверяем, что запросы уходят

Пишем базовую проверку и запускаем скрипт 👀

const api = new ChatGPTAPI({
  apiKey,
})

async function main() {
  const { text } = await api.sendMessage('Test connection');

  console.log(text)
}
Работаем!

Достаем изменения с помощью git-а.

Теперь, нам нужно собрать список изменений в файле. Для этого используем встроенный в Node модуль child_proccess

Запишем в переменную наш diff и отправим в GPT-3 уже полноценный запрос, который состоит из изменений и промпта.

import { execSync } from 'child_process';
import { ChatGPTAPI } from 'chatgpt'

// Здесь пропущена инициализация клиента, она есть выше.

async function main() {
  const diff = execSync('git diff --staged').toString()

  const prompt = 'Generate a short commit title based on diff changes above, using gitmoji and conventional commits. Structure: <emoji> <type>: <subject>'

  const { text } = await api.sendMessage(`${diff}\n # ${prompt}`)

  console.log(`Proposed Commit:\n------------------------------\n${text}\n------------------------------`)
}

main();

И попробуем добавить в гит эти изменения и запустить скрипт.

Сообщение не очень информативное, но мы видим, что скрипт работает

Добавляем вариативность

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

// Мы получили коммит сообщение в переменную text выше.

inquirer
    .prompt([
      {
        type: 'confirm',
        name: 'continue',
        message: 'Do you want to continue?',
        default: true,
      },
    ])
    .then((answers) => {
      if (!answers.continue) {
        console.log('Commit aborted by user.');
        process.exit(1);
      }
      // info('Committing Message...');
      console.log('Committing Message...')
      execSync(`git commit -F -`, { input: text }); // Делаем коммит с текущим сообщением. 
    });

Застейджим изменения и попробуем вызвать наш скрипт.

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

Модифицируем промпт

Если вы уже пробовали работать с ChatGPT, то понимаете, что самая сложная часть для работы с ИИ – уметь правильно обьяснить задачу. В моем примере обьяснение выглядит понятным для человека, но ИИ в большинстве случаев выдает просто название измененного файла. Воспользуемся примером из репозитория с промптами и попробуем более детально описать то, что мы хотим получить.

Вот новый prompt:

I want you to act as a senior Frontend developer. I will provide you with my code changes as a git diff and ask you to generate a commit message. In our project, we use conventional commits and gitmoji to design the messages. The commit structure should be in the form of <emoji> <type in lowercase>: <subject>\nHere is a list of changes:\n

Добавим его в проект и попробуем сгенерировать сообщение.

Со второго раза получилось вполне осмысленное сообщение 😎

Добавим еще немного функциональности

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

Реализуем логику работы с list. Вот новая функция, которая будет вызываться при добавлении параметра --list в вызов.

const generateListCommits = async (diff) => {
	// Добавили новый промпт с 5 вариантами.
  const prompt = 'I want you to act as a commit message generator. I will provide you with my code changes as a git diff and I would like you to generate an appropriate commit message. Generate 5 variants and send them in one line, separated by commas. Try to understand the meaning of the changes, not just the name of the file. In our project, we use conventional commits and gitmoji to design the messages. The commit structure should be of `<emoji> <type in lowercase>: <subject>`\nHere is a list of changes:\n'

  const { text } = await api.sendMessage(prompt + diff)

  const msgs = text.split(',').map((msg) => msg.trim())

  // add regenerate option
  msgs.push(REGENERATE_MSG)

  inquirer.prompt([
    {
      type: 'list',
      name: 'commit',
      message: 'Select a commit message',
      choices: msgs,
    },
  ]).then((answers) => {
    if (answers.commit === REGENERATE_MSG) {
			// На всякий случай добавим рекурсию, если все 5 вариантов оказались неподходящие
      generateListCommits(diff) 
      return
    }
    console.log('Committing Message... 🚀 ')
    execSync(`git commit -F -`, { input: answers.commit });
    console.log('Commit Successful! 🎉')
  });
}

С force все ещё проще, мы просто убираем возможность отклонить коммит. Вот итоговый код

const generateSingleCommit = async () => {
  const prompt = 'I want you to act as a commit message generator. I will provide you with my code changes as a git diff and I would like you to generate an appropriate commit message. Try to understand the meaning of the changes, not just the name of the file. In our project, we use conventional commits and gitmoji to design the messages. The commit structure should be of `<emoji> <type in lowercase>: <subject>`\nHere is a list of changes:\n'

  const { text } = await api.sendMessage(prompt + diff)

  console.log(`Proposed Commit:\n------------------------------\n${text}\n------------------------------`)

  if (args.force) {
    makeCommit(text)
    return
  }

  inquirer
    .prompt([
      {
        type: 'confirm',
        name: 'continue',
        message: 'Do you want to continue?',
        default: true,
      },
    ])
    .then((answers) => {
      if (!answers.continue) {
        console.log('Commit aborted by user 🙅‍♂️');
        process.exit(1);
      }

      makeCommit(text)
    });
}

Рефакторим

Какой код без рефакторинга? Плохой!

Спустя пару дней тестирования пакета я понял, что генерация gitmoji работает не так, как я ожидал. В большинстве случаев emoji выбирается рандомно из списка допустимых в gitmoji. Упростим prompt, а подставление правильного gitmoji оставим в виде обычной javascript функции.

Для этого воспользуемся ChatGPT и попросим написать функцию за нас.

Используем

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

Я опубликовал пакет по адресу https://www.npmjs.com/package/ai-commit и теперь, для того чтобы его использовать на любом компьютере, мне нужно сделать 3 вещи.

  1. Установить глобально env переменную с моим OPENAI_API_KEY. Я добавлял ключ на маке по этой инструкции , для своих ОС можете попросить совет у ChatGPT (ну или загуглить, как бумеры)
  2. Установить глобально пакет: npm install -g ai-commit
  3. Воспользоваться библиотекой в любом месте, где вы совершаете изменения с помощью команды ai-commit
  4. Вы великолепны ✨

Упрощаем, Level GOD!

  1. Добавляем в VSCode расширение Command Runner (https://marketplace.visualstudio.com/items?itemName=edonet.vscode-command-runner)
  2. Нажимаем Cmd + Shift + P и ищем пункт Open Keyboard Shortcuts (JSON)
  3. В конце списка шорткатов добавляем еще один, с таким содержимым:
{
    "key": "cmd+enter",
    "label": "Generate commit and push",
    "command": "command-runner.run",
    "args": {"command": "git add . && ai-commit"},
    "when": "editorTextFocus"
  }

Теперь мы на любое небольшое изменение просто нажимаем Cmd + Enter и получаем готовый коммит. Добавлять файлы вручную для этого не обязательно.

Заключение

И так, мы подошли к концу статьи. Я надеюсь, что вам удалось получить некоторые полезные советы и идеи, которые помогут вам стать более продуктивным и эффективным в своей работе.

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

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

Я надеюсь, что моя библиотека и советы помогут вам достигнуть новых высот в вашей работе. Желаю вам удачи и успехов в вашей профессиональной деятельности! Подписывайтесь на мой телеграм канал, ставьте лайки и до скорой встречи 💜