Разработчик ревьюит код ChatGPT
Разработчик ревьюит код ChatGPT

О чём статья?

Статья о реалиях и перспективах использования ChatGPT для автоматизации написания Java кода на конкретном примере и несколько выводов и тезисов, как ChatGPT изменит рынок разработки.

Введение

Касательно практики использования ChatGPT сегодня и перспектив использования генератора кода в ближайшем будущем есть диаметрально противоположные мнения среди разработчиков и руководителей IT фирм: одни пророчат отъём хлеба Chat’ом у программистов уже завтра, другие не видит в GPT конкурента человеку даже в перспективе ближайшей четверти века.

Первые в качестве доказательств своей правоты, как замечательно ChatGPT заменяет труд человека, приводят пару видео-примеров, где в реал-тайме с помощью ChatGPT пишут код простенькой игры всего за 10 минут, а также в поддержку своего мнения упоминают исторические параллели с автоматизацией в других сферах, где прогресс сократил участие человека в 5-7 раз.

Второй же лагерь оппонирует первым показывая видео, где люди пишут сервисы и игры тоже за 10 минут и без всякой помощи ChatGPT, приводя иные доводы незаменимости человека в процессе разработки самыми популярными среди которых являются:

  • важная роль коммуникации человека,

  • трудности формулирования заказчиком своих требований,

  • необходимость творческого процесса кастомизирования приложения, которое может выполнить только непосредственно человек.

Разработчик в восторге от перспектив, которые открывает нам ChatGPT
Разработчик в восторге от перспектив, которые открывает нам ChatGPT

Так кто же прав? Когда уже искусственные нас всех «позаменяют»? И с чем уместнее сравнить ChatGPT сегодня?

Буквально пара слов о теории, чтобы понимать практику

Для обработки запроса по написанию кода ChatGPT перелопачивает огромное количество данных, источниками которых в том числе являются открытые репозитории с кодом, где уже, скорее всего, есть элементы искомого нами решения, либо само решение целиком. И, имея внутренний граф, который опирается на эти данные, модель ходит по его «лабиринту» пытаясь предсказать вероятность определяющую уместность лексических единиц (переменных, методов, кусков кода), которые выстраиваясь последовательно друг за другом, составляют код приложения, являющегося ответом на наш запрос.

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

Как происходит работа с ChatGPT

Можно полностью взять большую задачу и скормить её ChatGPT as it is. Он скушает, выдаст решение. Но чем большей по объёму будет задача, тем:

  1. Более абстрактным и типизированным будет решение, нуждающееся в кастомизации,
    или

  2. Тем более неподходящим будет решение, требующее переработки,
    либо

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

Про занимательные вечера полные дебага и нервотрёпки я вообще молчу.

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

Давайте именно такую и попробуем решить.

Задача

Есть HttpServletRequest request, который содержит в теле запроса JSON. Нам нужно извлечь его из запроса и конвертировать с помощью библиотеки Gson в Java объект.

Решение

Как и говорится во всех best practice: максимально лаконично формулируем задачу на английском и задаём её ChatGPT, который в свою очередь даёт нам вариант решения. Если вариант не устраивает, то запрос редактируем, уточняем, дополняем, делаем снова и получаем новый вариант. Ниже как раз и приведены эти варианты:

Первый вариант, неподходящий

Запрос: There is an object of Java class we know in HttpServletRequest. What is the best way to get it using Gson parsing?

Oтвет: If you have an object of a Java class in HttpServletRequest and you want to get it using Gson parsing, you can use the following code:

import com.google.gson.Gson;
import javax.servlet.http.HttpServletRequest;

public class JsonUtils {
    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) {
        try {
            Gson gson = new Gson();
            String jsonString = gson.toJson(request.getAttribute("attributeName"));
            return gson.fromJson(jsonString, clazz);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Имеется также описание под кодом, но мы его будем опускать.

Обычно, когда мы взаимодействуем с сервером, то передача объекта осуществляется в теле запроса. В данном случае предлагается брать из... атрибута? Оригинальный case.
Это как раз и есть то самое «маловероятное» решение, которое нам посчастливилось получить, где ChatGPT выбрал отнюдь не самый популярный вариант. Это подтверждается тщетными многократными попытками повторить этот ответ.

Можно было бы уточнить в следующем запросе ChatGPT, откуда именно мы собираемся получать JSON из HttpServletRequest, но этого делать не будем, слишком большой запрос тоже может привести к крайне неожиданным результатам, да и case который предлагал Chat несколько девиантный. В следующем примере мы разве что уточним, чтобы решение нам выводилось утилитным методом, потому что иногда мы будем получать решение в методе doGet.

Второй вариант, ужасный

Запрос: There is an object of Java class we know in HttpServletRequest.What is the best way to get it using Gson parsing?Write it as util method.

Oтвет: Sure, here's an example utility method that you can use to get an object of a known Java class from HttpServletRequest using Gson parsing:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        Gson gson = new Gson();
        BufferedReader reader = request.getReader();
        T object = gson.fromJson(reader.lines().collect(Collectors.joining()), clazz);
        return object;
    }
}

Мы вставляем это решение в наш код. Всё работает. Можно закрывать бота ChatGPT, писать код дальше. Но это будет ошибкой.

В данном коде мы не закрываем ресурсы, которые у нас открываются при работе с BufferedReader. А это надо делать не где иначе как в этом самом методе, который нам ChatGPT выдал как решение. Но может он так не считает? Давайте спросим.

Третий вариант, приемлемый

Запрос: Aren't we supposed to close the BufferedReader?

Oтвет: Yes, you are correct. It's important to close the BufferedReader after reading the contents of the request. Here's the updated code snippet with the BufferedReader properly closed:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        Gson gson = new Gson();
        BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()));
        T object = gson.fromJson(reader, clazz);
        reader.close();
        return object;
    }
}

В ответе подтверждается, что нужно вызывать метод close().

Если мы сделаем профилирование, передавая JSON объект средней тяжести, то увидим, что действительно, GC отрабатывает в 1,5 раза реже, максимальная память используется тоже процентов на 10 меньше.

Но подождите, Java 8 релизнулась уже очень давно! interface Closeable наследует
AutoCloseable, а значит, мы можем сделать try-with-resources.

Четвёртый вариант, более подходящий

Запрос: Can you use try-with-resources syntax?

Oтвет: Yes, you can use the try-with-resources syntax to automatically close the BufferedReader after reading the contents of the request. Here's the updated code snippet with the try-with-resources syntax:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        Gson gson = new Gson();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream()))) {
            T object = gson.fromJson(reader, clazz);
            return object;
        }
    }
}

Вроде теперь у нас и ресурсы закрываются, и try-with-resources мы используем. Но взгляните повнимательнее. Если мы вставим данный код в IDE, та начнёт нам подсвечивать T object, предлагая сразу возвращать результат переменной. Да и немного странно, что получаем из request inputStream, оборачиваем в InputStreamReader и уже после вставляем в BufferedReader.

Можем это сделать сами, но зачем нам это делать, если есть ChatGPT?!)

И тут мы делаем одну интересную вещь. Мы не просто пытаемся уточнить
специфический параметр, мы пытаемся ему как-то намекнуть, чтобы он сделал хорошо).

Пятый вариант, самый оптимальный

Запрос: There is an object of Java class we know in HttpServletRequest.What is the best way to get it using Gson parsing?Write it as util method. Make good working efficient code.

Oтвет: Sure, here's an updated utility method that uses the try-with-resources syntax and is optimized for performance:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    private static final Gson gson = new Gson();

    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        try (BufferedReader reader = request.getReader()) {
            return gson.fromJson(reader, clazz);
        }
    }
}

Ну вот, в принципе, это тот самый вариант, когда можно было бы остановиться. Чётко, красиво, лаконично.

Самый шустрый вариант согласно профилированию, что я проводил. Переменная вынесена, inline сделан, а в BufferedReader мы сразу получаем reader, а не делаем это через InputStream.

Но давайте ещё посмотрим, какие варианты мы можем получить, спекулируя на «сделай получше», «максимально эффективно».

Шестой вариант, ничем не отличающийся

Запрос: There is an object of Java class we know in HttpServletRequest.What is the best way to get it using Gson parsing?Write me such a good code that could be used in production, efficiently using system resources and having maximum performance.

Oтвет: To get an object of a known Java class from HttpServletRequest using Gson parsing, the best way is to use the BufferedReader class and the fromJson() method of the Gson class. Here's an example code snippet that you can use in production:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    private static final Gson gson = new Gson();

    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        try (BufferedReader reader = request.getReader()) {
            return gson.fromJson(reader, clazz);
        }
    }
}

Мы попросили сделать такой код, который бы мы могли использовать в продакшене. В данном конкретном случае здесь не было изменений, но! иногда данный лайфак работает. Порой, когда указываешь prod, production, то сгенерированный код может быть реально сделан более уместно для работы в ПРОМе, а не просто в каких-то тестовых приложениях.

Седьмой вариант. Java17-й

Однажды я спрашивал у Чата про то, в каком виде Java хранит данные в классе String. Ответ был: "в виде массива символов". Но ведь это было ещё в 8-й версии. Сейчас в тренде более новые LTS 11, 17. Поэтому, уточнив версию Java я получил ответ: "в виде массива байт".

Запрос: There is an object of Java class we know in HttpServletRequest.What is the best way to get it using Gson parsing?Write me a code that uses resources as efficiently as possible and takes into account all the functionality of the Java 17

Oтвет: To get an object of a known Java class from HttpServletRequest using Gson parsing in Java 17, the best way is to use the BufferedReader class and the fromJson() method of the Gson class. Here's an example code snippet that you can use:

import com.google.gson.Gson;
import jakarta.servlet.http.HttpServletRequest;

import java.io.BufferedReader;
import java.io.IOException;

public class GsonUtils {
    private static final Gson gson = new Gson();

    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        try (var reader = request.getReader()) {
            return gson.fromJson(reader, clazz);
        }
    }
}

Как мы видим, тут больше речь не про производительность как таковую, а про то, чтобы использовать возможности новых версий. В частности, BufferedReader заменился на var.

Мы уже искали самый эффективный код. Давайте посмотрим, а может нам достаточно было бы попросить просто хороший код?

Восьмой вариант, самый ужасный

Запрос: There is an object of Java class we know in HttpServletRequest.What is the best way to get it using Gson parsing?Please make good working code.

Oтвет: Sure, here's a code snippet that you can use to get an object of a known Java class from HttpServletRequest using Gson parsing:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    private static final Gson gson = new Gson();

    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        try (BufferedReader reader = request.getReader()) {
            String jsonString = reader.lines().reduce("", (accumulator, actual) -> accumulator + actual);
            return gson.fromJson(jsonString, clazz);
        }
    }
}

Как мы видим, изменилась стратегия. Для парсинга через Gson мы сначала получали BufferedReader, в который передавали InputStream, а потом получали reader из request’а напрямую.

А сейчас мы пытаемся сделать это из строки функционалом на более низком уровне, реализация которого, в принципе, уже сделана у Gson. Но вдруг у нас это получилось намного эффективнее?

Если профилировать данный вариант, то он хуже предыдущих в разы!

Девятый вариант, мечтательный

А теперь давайте побудем маленькими фантазёрками и попытаемся попросить Чат сделать такой код, чтобы сразу получить повышение и премию)

Запрос: There is an object of Java class we know in HttpServletRequest.What is the best way to get it using Gson parsing?Please write such a cool code that I will immediately get a promotion in the company and get a huge bonus.

Oтвет: I'm glad to hear that you're excited about this task! Here's a code snippet that you can use to get an object of a known Java class from HttpServletRequest using Gson parsing:

import com.google.gson.Gson;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;

public class GsonUtils {
    private static final Gson gson = new Gson();

    public static <T> T getObjectFromRequest(HttpServletRequest request, Class<T> clazz) throws IOException {
        try (BufferedReader reader = request.getReader()) {
            String jsonString = reader.lines().collect(Collectors.joining());
            return gson.fromJson(jsonString, clazz);
        }
    }
}

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

Вытекающие из примера советы по практике:

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

  2. Обозначать требования эффективности и наивысшей производительности. Чату по большому счёту всё равно, какой вариант выдавать: низкопроизводительный или высокоэффективный;

  3. Нужно помнить, что пока что решения учитывающие новый или относительно новый функционал не будут в приоритете вывода ответов, если мы хотим получить более современный и актуальный ответ, мы тоже должны объявить это в требованиях;

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

  5. Полученное решение нужно максимально критически разобрать. И при возникновении каких-либо вопросов и сомнений, адресовать их Чату;

  6. Несмотря на всё вышеизложенное нужно минимум 5 раз сформировать запрос с несколько изменёнными параметрами, чтобы иметь выбор из нескольких вариантов;

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

Так что же такое исходя из вышесказанного по сути ChatGPT?

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

Разработчик открывает для себя ChatGPT
Разработчик открывает для себя ChatGPT

Стоит также отметить забегая вперёд, что, как и в сказке джинн не может заменить главного героя.

Как решение с помощью ChatGPT влияет на рабочий процесс?

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

Давайте условно представим, что именно меняется в алгоритме повседневной модели разработки, если мы используем ChatGPT минимизируя катавасии на одном маленьком участке кода:

Изменения в алгоритме разработки, которые затрагивает ChatGPT
Изменения в алгоритме разработки, которые затрагивает ChatGPT

Как видно, ChatGPT заменяет часть процесса, но насколько соразмерны старая и новая части, судите сами.

Поиск альтернатив ChatGPT как инструмента для написания кода

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

Прогноз

– Оставит ли ChatGPT разработчиков со временем без хлеба?
– Нет! На сегодняшний день ChatGPT в производстве — это не инженер, это инструмент инженера.

– Автоматизирует ли он со временем труд по написанию кода и сократит этот этап разработки в разы?
– Да! Безусловно.

– Останется ли ChatGPT исключительно в привычном нам формате запрос-ответ в разработке?
– Нет! Безусловно эта модель, либо аналогичная ей будут интегрированы в IDE, либо в иной инструмент для проектирования, разработки и тестирования.

И, конечно же, вывод, к которому я прихожу:

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

Репозиторий, в котором тестировались методы ChatGPT

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


  1. raamid
    28.06.2023 05:57
    +2

    Многие уже наверное в курсе, но если кто еще не знает: если код сгенерированный ИИ не работает, то можно просто из консоли скопировать сообщение об ошибке и скормить его ИИ. Тогда ИИ выдаст новый, исправленный вариант программы.


    1. syrtin Автор
      28.06.2023 05:57

      Эта практика копирования ошибки из консоли, действительно куда лучше, чем писать "Твой код не работает, найди ошибку", на что можно получить ответ "У меня всё работает")))
      В своей практике я сталкивался с ошибками, в основном, при первом варианте работы, когда пытаешься скормить не маленькую задачу (которая тут рассмотрена), а очень объёмную. Но там помимо ошибок порой приходят такие фортели, что ошибка является не самой большой проблемой кода.


    1. IvanPetrof
      28.06.2023 05:57

      Даже на правильный код ему можно сказать, что "там ошибка", он извинится, признает, что действительно ошибся и "перепишет". И в этот раз там действительно может быть ошибка.


  1. Dotarev
    28.06.2023 05:57

    А можно ли сравнивать ChatGPT и Copilot? Если да — дотягивает ли Copilot до уровня ChatGPT или если платить - то за комм. версию последнего?


    1. Opaspap
      28.06.2023 05:57

      Попробуйте codeium , он бесплатный, мне хватает. Там гпт3 ещё чат встроен.


    1. syrtin Автор
      28.06.2023 05:57

      OpenAI создавшие ChatGPT тоже принимали участие в разработке Copilot. Он, является ярким примером интеграции в IDE, которую я упомянул. Как и его аналоги (например, JARVIS от Сбера), он больше ориентирован на помощь в подборе вариантов исходя из описания контекста в самом процессе кодинга.
      Механизм несколько другой, порой более уместный, но зачастую, если уже знаешь, что хочешь и как это хочешь, написание контекста занимает больше времени, чем если писать сам код.


  1. panzerfaust
    28.06.2023 05:57

    ChatGPT однозначно сократит дефицит низкоквалифицированных рабочих мест в сфере IT

    Пока что все эти утверждения выглядят как план кальсонных гномов: берем Чят -> ??? -> дефицит сокращен. Есть основания сомневаться, что джун + Чят учится заметно быстрее чем джун + whatever (гугл, стэк, ментор). А если нет буста в обучении джунов, то и нет влияния на рынок.

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


    1. syrtin Автор
      28.06.2023 05:57

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


    1. andrewilife
      28.06.2023 05:57

      Уже сокращает колво ждунов, но еще быстрее сокращает время на проекты.

      Всё только начинается, сингулярность впереди.