Как и большинство серьёзных спикеров в IT, я внимательно слежу за тем, какую роль могут сыграть в разработке ПО системы генеративного искусственного интеллекта. Думаю, возникновение больших языковых моделей (LLM) повлияет на разработку ПО примерно в той же степени, что и переход с ассемблера на первые высокоуровневые языки программирования. Чем дальше развиваются языки и фреймворки, тем сильнее абстрагируется наш код и, соответственно, возрастает продуктивность, но такие изменения пока не касаются самой природы программирования. При применении LLM – уже касаются, но я хочу поговорить о том, что LLM не только повышают уровень абстракции. Пользуясь ими, приходится учитывать, как выглядит программирование с использованием недетерминированных инструментов.
Как я уже говорил, высокоуровневые языки программирования вывели нас на совершенно новый уровень абстракции. Работая с ассемблером, я думаю о наборе инструкций для конкретной машинной архитектуры. Мне приходится продумывать даже самые простые действия, размышляя, как я буду осуществлять те или иные последовательности действий, записывая данные в нужные регистры. Но при работе с высокоуровневыми языками появилась возможность обдумывать программу как последовательность команд, в которой предусмотрены условные операторы, чтобы выбирать альтернативы для продолжения. Также в программе есть итерации, чтобы раз за разом применять команды к наборам значений данных. Многие элементы кода можно именовать, чтобы было ясно, что именно понимается под теми или иными значениями. Ранние языки программирования явно были в чём-то ограничены. Я начинал программировать на Fortran IV, где у операторов «IF» не было условия «ELSE». Поэтому мне приходилось как-то запоминать мои целочисленные переменные, и я начинал их с букв от «I» до «N».
Когда условия стали менее строгими, и код приобрёл блочную структуру («У меня после IF может идти более одной команды»), программировать стало проще и интереснее, но принципиально программирование осталось прежним. Сейчас я почти не пишу циклов, а инстинктивно передаю функции как данные — но по-прежнему общаюсь с машиной примерно в таком же стиле, как тогда давным-давно на дорсетских болотах, когда моим языком был Fortran. Язык Ruby гораздо более изощрённый, чем Fortran, но Ruby свойственен такой дух, которого нет у Fortran и машинных инструкций PDP-11.
Пока мне довелось лишь слегка поболтать с самыми современными генеративными инструментами, но я очень ими увлечён и люблю слушать, как друзья и коллеги делятся впечатлениями о работе с ними. Думаю, это очередное фундаментальное изменение: общение с машиной на языке промптов настолько же отличается от программирования на Ruby, насколько Fortran отличается от ассемблера. Но это не только большой прыжок в том, что касается уровня абстракции. Написав функцию на Fortran, я мог сто раз её скомпилировать, и каждый раз в ней всё равно проявлялись одни и те же баги. Используя большие языковые модели, приходится иметь дело с недетерминированными абстракциями, так что я не могу просто сохранить мои промпты в git и быть уверен, что они всякий раз будут давать одинаковое поведение. Как выразилась моя коллега Биргитта , мы не просто движемся вверх по лестнице абстракций, мы одновременно движемся вбок по направлению к недетерминизму.

Осваивая LLM как рабочий инструмент, требуется научиться обращаться с этим недетерминизмом. Это драматическое изменение, которое меня сильно воодушевляет. Думаю, какие-то вещи мы при этом потеряем и будем по ним тосковать, но и приобретём мы такие вещи, которые пока понятны лишь немногим. Такая эволюция в сторону недетерминизма — беспрецедентное явление в истории нашей профессии.
Комментарии (6)
vadimr
05.09.2025 11:22LLM не работает с семантикой и поэтому не представляет собой абстракцию. Это просто очень сложный и недетерминированный синтаксический сахар.
n0isy
05.09.2025 11:22Но ведь сахар это и есть новый уровень абстракции. К примеру async/await в js, это сахар к promise/then. И он облегчает как написание кода, так и читаемость.
vadimr
05.09.2025 11:22Уровень абстракции - это сама концепция асинхронных вызовов, имеющаяся у программиста. А непосредственно оператор async с точки зрения компилятора, конечно, является синтаксическим сахаром над promise и далее над машинным кодом.
Чем абстракция отличается от машинной реализации, расшифровывать, наверное, не надо.
Vitimbo
05.09.2025 11:22Только, когда мы пишем какой-то 'сахарный' оператор, то мы гарантированно получим несахарный результат и он будет только один. Даже, если он будет скомпилирован 1000 раз.
Представлять ллм в такой же роли неверно просто потому, что результат каждый раз будет отличаться. Также будут отличаться и его свойства по потреблению ресурсов и быстродействию. В некоторых случаях, мы ещё и не можем точно сказать, запустится ли вновь сгенерированный код, в принципе.
vadimr
05.09.2025 11:22Генератор случайных чисел придуман не вчера, и ещё в самом первом компиляторе Фортрана 1956 года его, кстати, использовали для выбора наилучших оптимизаций методом Монте-Карло.
Некорректный код, объявленный допустимым результатом - это, конечно, элемент новизны.
CryptoPirate
Что значит "поэтому"? От куда это?
В оригинале там просто "and".