Эволюция языков программирования
Эволюция языков программирования

Современная разработка погрязла в driven, first и based подходах, недавно этот зоопарк пополнился еще одним заморским зверем под названием AI-driven (пусть меня простят свидетели AGI, но я сознательно не выделяю этот подход на фоне остальных и в конце объясню почему). Но не пытаются ли все эти подходы на самом деле решить одну и ту же проблему, известную еще с середины прошлого века, проблему "абстрактного перехода"?

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

Первый переход

Hello world!
Hello world!

В период с 1942 по 1945 немецкий инженер Конрад Цузе создает первый в мире язык высокого уровня, Plankalkül, который уже содержит переменные, циклы, условные конструкции, массивы и кортежи, и это был первый в истории программирования абстрактный переход от бинарной, и любых арных кодировок к "человеко-читаемым" инструкциям, думаю тут в целом все более менее ясно, переход вполне однозначный. Но это был тот случай, когда идея появляется раньше, чем мир был к ней готов, зато служит прекрасной отправной точкой данной статьи.

С конца 40-х до начала 50-х специалисты в США пытаются решить аналогичную проблему, разрабатывая свои символические языки, в конечном счете это приводит к появлению целого семейства языков под общим названием ассемблер, языка хоть и не такого продвинутого, как Plankalkül, но имеющего индустриальную и коммерческую базу, а вот закрепился в железе и в деньгах первый абстрактный переход пожалуй с появления в 1957 году всем известного языка Fortran, если Plankalkül был переходом идейным, то Fortran подтвердил и развил эту идею на практике. Тут можно конечно возразить, что не язык совершил этот переход, а появление компилятора, но компилятор не возник бы без идеи языка следующего уровня, который нужно компилировать в язык предыдущего, потому он скорее служит исполнительным органом данного намерения.

Сутью первого абстрактного перехода был не конкретный язык, а появление принципа "код как человеко-читаемый текст", этот слой останется доминирующим надолго, из него будут выжимать максимум, в следующие 50 лет появится еще куча языков, пожалуй самыми заметными станут C, SQL, C++, Python, Java, JavaScript, все перечислять не буду. Тут важно отметить одно интересное свойство первого перехода, так как фундаментальный принцип, который он принес, это человеко-читаемость, то языки, которые унаследуют этот принцип будут бороться именно за человека, за право стать его главным инструментом, отсюда возникает идея языка общего назначения и многие будут пытаться им стать, пока сама идея не начнет себя исчерпывать и не появится новый кандидат на звание второго абстрактного перехода.

Второй переход

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

Декларативное программирование

Да, декларативные языки наделали много шуму, в 1972 году появляется замечательный язык Prolog, который в принципе можно считать первым по настоящему декларативным языком, ты пишешь запрос к набору фактов, а логический движок сам разбирается, как построить вычисления, но тут важно разобраться в сути декларативного программирования, его конечно отделяют от императивного подхода, но я попытаюсь показать, что это разделение отчасти фиктивно, и имеет скорее инженерную природу. Например, тот же Fortran позволял написать:

Y = A * X + B

разве это не декларация намерения, ты описываешь формулу, а не как управлять регистрами, а вот пример на языке ALGOL:

BEGIN    
  INTEGER PROCEDURE Factorial(n);    
  VALUE n;    
  
  IF n <= 1 THEN        
    Factorial := 1    
  ELSE        
    Factorial := n * Factorial(n - 1)    
  FI
END;

тут твоим намерением становится уже описание алгоритма и ты вполне декларативно его описываешь. Можем ли мы считать Fortarn и ALGOL декларативными языками? Да, можем! Просто в случае с этими языками наши намерения служат описанию формул и алгоритмов, Prolog же имеет более абстрактное основание - логика, но суть остается та же. Это разделение станет заметнее именно с появлением идеи языка общего назначения и усилением принципа не что, а как, но повторюсь, это не фундаментальный сдвиг, а скорее временная подмена понятий, поэтому полноценным абстрактным переходом я его считать не могу!

Архитектура и ООП

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

NATO Software Engineering Conferences
NATO Software Engineering Conferences

Конечно можно сказать, что деление кода на блоки в первых языках тоже архитектура, но я хочу быть честен и поговорить о том, когда архитектура программного кода становится самодостаточной и о ней говорят отдельно. В 1968-1969 года проходит конференция "NATO Software Engineering Conferences", на которой активно обсуждаются идеи дизайна ПО, ключевой вывод - системы стали слишком сложными, именно этот принцип и приводит к осознанию потребности в архитектурном подходе в написании программ, одна цитата из этой дискуссии очень красноречиво это описывает:

software designers are in a similar position to architects and civil engineers, particularly those concerned with the design of large heterogeneous constructions, such as towns and industrial plants. It therefore seems natural that we should turn to these subjects for ideas about how to attack the design problem.

Peter Naur

За несколько лет до этой конференции на свет появляется язык Simula, который первый предложит классы и объекты, из него вырастет целая религия ООП, кто-то его любит, кто-то нет, но как это связано с архитектурой, понятно станет дальше. А еще раньше язык COBOL, который предложит бизнес-ориентированный код. Казалось бы разные технологии, разные подходы, но цель при этом одна - моделирование реального мира. Но зачем вообще моделировать реальный мир, почему не ограничиться функциями, процедурами и алгоритмами? На этот вопрос отвечает все тот же первый абстрактный переход - для человека! Человек, не машина, он всегда стремится упростить то, что сложно, а любая банковская, корпоративная или государственная системы сложны по своей природе и писать для них софт через алгоритмы и процедуры все равно, что строить замок из спичек. Архитектура же пытается решить абсолютно ту же проблему, просто, когда сам язык не вмещает в себя конструкции, нужные для упрощения разработки, это просачивается наружу и реализуется через паттерны, фреймворки и стандартные библиотеки, но меняет ли это суть принципиально? Думаю нет, просто мы развиваем идею человеко-читаемых программ в идею человеко-понимаемых систем, но суть остается та же!

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

Итог

Целью первого абстрактного перехода был человек и в конечном счете любой язык или технология стали понятны человеку. НО! На одном и том же языке (понятном всем) можно написать игру, а можно биллинговую систему, но напишет ли сходу разработчик игр биллинговую систему и наоборот? По мере развития IT в него заходили новые компании и приносили свои предметные области и по сути произошла тихая доменная революция, это был момент, когда человек перестал быть центром разработки, им стала предметная область. Назвать конкретную дату тут конечно сложно, так как ориентация на предметную область случалась и раньше, но в какой-то момент они усложнились настолько, что современный разработчик уже не просто должен знать язык, он вынужден знать доменный стек.

Поняв цель нового перехода, давайте ретроспективно посмотрим, как он повлиял на сами языки, мы ведь ради программирования все это затеяли! Возьмем SQL и Golang, что у них общего? Оба решают разные задачи, имеют разный синтаксис и принадлежат разным парадигмам, но они оба достаточно сильно зависят от своих областей применения, то же становится верным и для остальных! Но есть же полноценные языки общего назначения, как быть с ними? Так как эти языки возникли в первый переход их нынешняя универсальность инерциальна и остается ровно до тех пор, пока не появляются новые pain-killer-ы и не откусывает свои части пирога. Возьмем например C++, Rust забирает performance, делая его безопаснее, Go серверную разработку. Или Java, Kotlin вытесняет её из Android, Scala забрал часть серверной и data-инженерии, тот же Go забирает инфраструктурные и backend задачи, в общем список можете продолжить сами, он большой.

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

Предметная область усложняется в основном горизонтально. Возьмем простой пример, веб-сайт. В 90-е годы это было относительно простое клиент-серверное приложение, причем и клиент, и сервер были достаточно тонкими. Один человек вполне мог держать всю систему в голове. Сегодня клиент того же веб-сайта, это уже десятки фреймворков и подходов, бэкенд и того хуже: множество баз данных под разные задачи, контейнеризация, оркестрация, кеширование, очереди, CI/CD, инфраструктура, мониторинг, безопасность. Тут можно возразить, что нагрузки выросли кратно, отсюда и сложность системы, но это лишь часть правды. Да, системы стали обрабатывать больше данных, пользователей и запросов, но рост нагрузки сам по себе не объясняет такого разнообразия технологий. Если бы дело было только в нагрузке, мы бы увидели эволюцию вглубь: более мощные базы данных, более быстрые серверы, более эффективные алгоритмы. Но вместо этого мы видим взрыв вширь, десятки альтернативных решений для одной и той же задачи, каждое со своим подходом, своими ограничениями и своим языком. Более того, подавляющее большинство проектов никогда не достигает масштабов, требующих такой сложности, но при этом используют те же инструменты. Это означает, что сложность стала не следствием нагрузки, а следствием самой раздутой экосистемы.

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

Но если это так, то возникает вопрос: действительно ли предметная область стала выше человека, или же мы просто потеряли способ работать с ней как с единым целым?

Третий переход

И вот тут интересно! Скажу сразу, третий переход, наступает на пятки сильно быстрее второго, потому что управлять этим зоопарком сложно. На этом фоне возникают различные подходы, направленные на решение этой проблемы, как раз всякие driven, first и based подходы, но решают они ее только отчасти, да, где-то код становится писать проще, что-то генерится автоматически, но все они имеют свой base слой, который также часто зависим, например API-first предлагает нам воспринимать ПО как придаток к API, но API явно не тянет на универсальный язык, так как некоторые системы монолитны по своей сути, например игры. Тогда может Domain-driven? Нет, потому что бизнес - очень слабая абстракция, один и тот же бизнес может иметь множество разных форм реализации на земле и лепить из этого единый язык все равно, что пытаться найти лжеца в игре, где лгут все. Тогда наверное AI-driven, уж он то точно предлагает реальную независимость! Не совсем, но давайте разбираться!

Искусственный интеллект

AI-driven подход состоит из двух элементов: язык и сам AI, начнем с языка. Как уже писалось в прекрасной статье "Почему наш язык — худший язык для программирования" естественный язык, на котором мы сейчас общаемся с ИИ не подходит, но что если создать достаточно строгий, формальный язык общения с ИИ? Ну он конечно уже создается, например CodeSpeak, но представим, что мы создали прям идеальный язык "программирования" ИИ, неужели мы тогда действительно совершим третий переход? Тут перейдем ко второму элементу - самому ИИ.

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

Современные LLM содержат в себе миллиарды параметров, и эти параметры отражают знания из огромного корпуса данных в том числе и программного кода. Миллионы репозиториев, тысячи подходов, бесконечное количество реализаций одних и тех же идей, всё это попадает внутрь модели. Если посмотреть глубже, то пространство программирования устроено неравномерно. С одной стороны, у нас есть относительно ограниченное количество фундаментальных сущностей: принципы, алгоритмы, паттерны и вряд ли их очень много. С другой стороны, у каждого из них есть множество проекций на конкретные технологии и вот это пространство уже растет экспоненциально. LLM работают именно в этом расширенном пространстве. Они не оперируют напрямую принципами, они оперируют их реализациями. В результате одна и та же идея может быть представлена десятками, а иногда и сотнями различных вариантов, от удачных до откровенно слабых. Модель не знает, какая из них лучшая в абсолютном смысле, она лишь оценивает вероятность той или иной последовательности в заданном контексте. И здесь возникает ключевой момент. LLM отлично справляются с ролью навигатора: они помогают быстрее находить решения в огромном пространстве реализаций, подсказывают варианты, ускоряют разработку. Но они не уменьшают само это пространство. Если же мы хотим предсказуемости и гарантированного качества, нам приходится отбрасывать неэффективные реализации, или как-то заставлять ее применять эффективные. Но тогда, если нас интересует однозначное соответствие эффективный принцип - эффективная реализация, то зачем нам система, которая по своей природе устроена иначе? На самом деле у меня есть свое видение третьего перехода.

Мета-язык

Что еще за мета-язык и зачем он вообще нужен? Давайте разбираться!

Как я уже сказал, есть проблема избыточности решений, вызванная вторым переходом, язык + LLM не работает, что же делать? Неужели мы и вправду в конце времен и нам суждено утопать в этом алом океане? Не совсем!

Скажу сразу, я разрабатываю свой мета-язык, о котором расскажу в следующей статье, а пока продолжим!

На самом деле мета-язык это не что-то новое, история уже помнит некоторые попытки создания подобных языков, в той же дискуссии "NATO Software Engineering Conferences", а это 1969 год, уже говорилось о неком SSL (Software Specification Language), но тогда не было конкретных предложений, только намеки. При этом за несколько лет до этого уже была придумана методология VDM (Vienna Development Method), а в 1970 году она обзавелась своим языком VDM-SL, который важен лишь в контексте истории и разумеется полноценным мета-языком не является. Но что же дальше?

А дальше рождается куча как языков спецификации, так и языков конфигурации, каждый из которых решает частные задачи: кто-то для формального описания требований, кто-то для генерации кода, кто-то для автоматизации инфраструктуры. В 80–90-е годы появляются CASE-средства, языки вроде SDL (Specification and Description Language), а также развивается идея визуального описания систем, которая в итоге приводит к появлению в 1995 году языка UML. UML стал, пожалуй, одной из самых амбициозных попыток подняться не только над кодом, но и над предметной областью, но на практике он так и не стал универсальным языком разработки, оставшись инструментом документации и проектирования. Дальше уже возникают более прикладные форматы, которые постепенно становятся частью инженерской повседневности. В 1998 году появляется XML, язык разметки для структурированных документов, который быстро превратился в универсальный инструмент обмена данными между системами. В ответ на громоздкость XML возникают более легковесные форматы: JSON в 2001 году становится стандартом для веб API и клиент-серверного взаимодействия, YAML чуть позже завоевывает популярность в DevOps. Не отстают и другие форматы, TOML появляется как удобная разметка конфигурационных файлов, Proto дает бинарный и компактный способ сериализации данных, а внутри инфраструктуры и облачных платформ рождаются свои языки и инструменты: HCL у Terraform, Kustomize и Helm Charts у Kubernetes. Каждый из этих языков и форматов решает узкую задачу: кто-то описывает структуру данных, кто-то инфраструктуру, кто-то бизнес-правила. Но при этом ни один из них не умеет объединять все эти уровни в единую систему. Так как все эти мета-языки появились в основном во втором переходе, они дают мощные инструменты для своего домена, но проблему хаоса разумеется не решают.

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

Давайте попытаемся сформулировать эти требования! Я предлагаю следующее:

  1. Принципиальная абстракция: язык должен быть независим от конкретных предметных областей, языков и технологий, но при этом позволять описывать их внутри себя.

  2. Уровни абстракции: развивая первый пункт, язык должен позволять описывать произвольные уровни абстракции, не смешивая их между собой.

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

  4. Формальность: язык должен быть достаточно строгим и формальным, чтобы любые конструкции имели однозначную интерпретацию и могли быть проверены автоматически.

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

  6. Компактность: программа на этом языке должна быть сильно проще и короче, в идеале в разы, чем на языке второго уровня абстракции, мы ведь упростить себе жизнь хотим!

  7. Предсказуемость: результат компиляции должен быть детерминированным, один и тот же код всегда приводит к одному и тому же результату.

  8. Интероперабельность: язык не должен ломать существующую экосистему, а наоборот, уметь в нее встраиваться.

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

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

@NekitaKamenev

Искренне благодарю вас, что дочитали до конца!