Почему мы пишем и храним код в текстовых файлах?

2e297bbd4d104713b2cbcacbdf33b027.jpg

Простые текстовые форматы данных — прекрасная штука. Нет, без шуток. Возьмём, например, банальные txt-файлы. Ну красота же! В любом утюге есть текстовый редактор, можно открыть файл, почитать, записать. Любой уважающий себя язык программирования из коробки даст вам средства работы с текстовыми файлами. Или вот ранние сетевые протоколы — SMTP, POP3, HTTP 1.0. Это вообще такое блаженство, что слёзы на глаза наворачиваются. Можно взять telnet, приконнектиться к серверу, отдавать команды и читать ответы! Писать клиенты, серверы, снифферы, прокси — одно удовольствие.

Но время не стоит на месте и удобство работы программиста, к сожалению (или к счастью!), давно перестало быть главным критерием выбора технологий. В прекрасные текстовые файлы понадобилось вдруг добавлять графики, картинки и ссылки — и вот у нас есть форматы pdf и doc. С сетевыми протоколами вообще беда — людям зачем-то понадобились сжатие трафика, шифрование, мультиплексирование и другие страшные вещи. И вот уже у нас есть HTTP/2, с которым телнетом не очень-то поработаешь. Даже всем красивый REST разные негодяи типа Google нет-нет, да и пытаются заменить на какой-нибудь gRPC. И это я ещё не начал рассуждать о том, что мало что нынче сохраняется текстовые файлы — все почему-то используют какие-то базы данных, совершенно нечитабельные при открытии их текстовым редактором, но какой-то магией позволяющие информацию структурировать, индексировать, эффективно искать и т.д.

И вот здесь мы подходим к теме того, что я хотел бы обсудить. Как видите, все форматы хранения данных, протоколы и прочие штуки прошли длинный путь в поисках своего оптимального вида. Однако мы, программисты, продолжаем писать код наших программ в текстовых файлах, как делали это наши дедушки лет 50 назад (или уже больше?). Какую бы ОС мы ни использовали, какой бы модный язык ни выбрали, как бы ни была крутой наша IDE — результатом написания кода будут буквы в текстовом файле. Что, согласитесь, в теории совершенно не обязательно, ибо наши буквы компьютеру нужны как зайцу стоп-сигнал. Ему нужны машинные команды для выполнения, это просто нам удобнее описывать их какими-то там буквами. А на самом деле ведь «цикл» или «функция» будут тем, чем они являются, как ты их не назови и где ты их не храни. «Роза пахнет розой, хоть розой назови её, хоть нет».

Да, мы привыкли писать код в текстовые файлы. Вы открываете файл — ну и вроде бы видите код своей программы. Какую-то его часть, вернее. Как-то видите. Понимаете. Ну или думаете, что понимаете. Но вот пост на Facebook вы тоже открываете, видите и понимаете —, а ведь Facebook точно этот пост не в текстовом файле хранил.

Давайте же посмотрим, какие проблемы нам создаёт хранение кода в виде набора текстовых строк.

Всё это страшно неудобно обрабатывать всем используемым нами инструментам
Весь тот код, который мы напишем, в последствии будет читать куча программ: IDE, её плагины, компилятор, дебаггер, средства статического анализа, контроля версий, билд-сервер и т.д. Всем им вот просто невероятно нравятся текстовые форматы (на самом деле — нет). Каждый из них будет их читать, анализировать (т.е. вы уже видите бесполезный труд десятков команд разработчиков, для каждого языка программирования), тратить на это время. А ещё они иногда будут ошибаться, ну или не ошибаться, а понимать ваш код слегка по разному. Не секрет, что IDE и какой-нибудь её плагин могут предложить разное автодополнение и разные средства рефакторинга для одного и того же кода. А как было бы хорошо, если бы один инструмент один раз превратил написанные вами буквы в некоторое AST-подобное представление (возможно, расширенное, с метаданными) и сохранил это всё в базе, которую мы и будем называть «кодовой базой проекта». А дальше уже каждый инструмент обращался бы к этой базе, по определённым протоколам, получая стабильные, стандартизированные и предсказуемые результаты. Ближайшим приближением к идеалу, о котором я на сегодняшний день знаю, является проект от Google, называющийся Kythe. Там ещё далеко до идеала, но направление верное.

Всё это страшно неудобно читать и писать людям
«Что может быть понятнее текста?!» — спросите вы. Да много чего может. Текст — хорошее средство представление информации, но не единственно возможное, и не всегда лучшее. Посмотрите на нынешний интерес к книгам — существенно упал. Потому, что многие представление информации воспринимаются нами живее, быстрее. Тот же аудиовизуальный ряд. Или инфографика. Или даже текст —, но не сплошной стеной, а как-то структурированный, отсортированный, сгруппированный и отфильтрованный. Да, мы можем сделать наш код таким. Если вот сами поставим себе эту цель, сядем и сделаем. И делаем ведь — приходится, других вариантов нет. А как было бы классно получить всё это даром, автоматически. Иметь возможность увидеть код таким, как его хочется видеть. Чтобы написать SELECT по коду — и увидеть только те артефакты, которые соответствуют параметрам запроса. Или представить пересылку данных между двумя узлами в виде диаграммы. Или анимации. Поискать функцию не по названия, а по воспоминанию «там было больше трёх параметров и она меняла второй из них». Или по тому, что в ней было три цикла. Или «она что-то делала со временем». Есть у нас сейчас такие инструменты? Нет их! Сиди и пиши регулярные выражения, как дурак.

С написанием кода дело ещё хуже, чем с чтением. Кто-то из программистов изучает слепой набор текста, кто-то зубрит хоткеи Vim, кто-то тыкает мышкой по менюшкам любимой IDE. То, что нам приходится этим заниматься — это ужас! Создание алгоритмов — это искусство, это магия, это акт творения. А мы берём свои каменные топоры и идём с их помощью строить звездолёт. А у нас нет сегодня ничего, кроме этих молотков. И каждый день появляются новые молотки, которые существенно меняют форму ручки и тип крепления к ней булыжника, на самом деле ни на йоту не приближая нас к тем инструментам, которыми должны строится звездолёты.

Стили форматирования кода
Вы помните холивары «табы против пробелов» на Хабре? Вот это были сражения. «Стиль форматирования кода» — это понятие, которое, почему-то существует, хотя его существовать не должно в принципе. Программисты его выбирают для своего проекта, стараются соблюдать (тратят время на это), иногда даже друг друга попрекают, если кто-то ненароком нарушает. Да что за бред? Представьте себе идеальный мир, где код хранится в бинарном файле, в виде связного набора сущностей, обозначающих классы, методы, циклы, переменные, условия. В этом случае каждый программист может настроить свой инструмент просмотра этого кода таким образом, чтобы ему рисовались хоть табы, хоть пробелы, хоть иконки котят. Все эти «на какой строке фигурная скобка» и «нужен ли пробел после запятой» просто ушли бы в прошлое. Точно так же при сохранении кода в базу он преобразовывался бы из кода, оформленного в стиле данного программиста, в просто код без форматирования. А потом импортировался другим программистом в его IDE в удобном ему виде.

Метаданные в коде
Недавно на хабре промелькнул пост о том, что аннотациям — не место в Java-коде. Конечно, им там не место. Потому, что это не код. Это метаданные. Мы пишем их в код, потому что больше некуда. Ну разве что в другой XML-файл рядом, что ещё хуже. Аннотация в Java, как и декораторы в Python, аттрибуты в C#, комментарии к методам в любом языке и т.д. — должны быть метаданными. У нас должны быть удобные инструменты поиска по этим метаданным, быстрого их включения\отключения, рефакторинга. Конечно, должна быть возможность показать их «как сейчас, в коде». Но «вшитие» их в код — это как каждую понравившуюся картинку сразу выбивать себе на груди татуировкой. Слишком кардинально, слишком большие последствия.

Аспектно-ориентированное программирование
Каждый, кто впервые читает об аспектно-ориентированном программировании, проникается красотой идеи. Мне оно нравится даже больше функционального. Но постойте, а где оно в реальном мире? А нет его. Потому, что аспектно-ориентированное программирование попросту не ложится на концепцию кода, записанного в виде последовательности текстовых строк. Попытки написать хоть что-то в аспектном стиле либо уводят нас в дебри рефлексии, либо требуют массовых правок, пронизывающих всю кодовую базу и не поддающихся автоматизированию.

А теперь представьте, что наш код эффективно сохранён в структурированный формат. И у нас есть инструмент для работы с ним. Итак, нужно добавить логирование всех входных параметров всех методов всех классов? Да легко, одна команда на изменение атрибутов всех методов (что-то типа «SET logging=true Where item=«method») + 1 строчка кода, определяющая формат лога. Точно так же в два клика это убирается. Что там ещё нам нужно делать в каждой функции? Профилирование? Аутентификация и проверка прав доступа? Проверять контракты? А почему бы и нет, если вот они — все наши классы, методы и добавление всего вот этого НЕ МЕНЯЕТ ИХ КОД. Добавили, потестировали, возможно убрали, а возможно оставили. А может быть сохранили несколько наборов аспектов (что-то для дебаг-окружения, что-то другое для продакшена).

Большой размер кодовой базы
Google недавно рассказывал, что у них 100 млн строк кода (уже, наверное, больше). Так много, наверное, ни у кого больше нет, но 1–2 миллиончика строк для энтерпрайз-проекта крупной организации уже является вполне обыденной вещью. И как вы, скажите пожалуйста, по этой кодовой базе навигируетесь? Насколько хороши в ней ваши любимые grep и find? А функционал вроде Find References? А средства рефакторинга? Текст — медленная и неуклюжая штука. Каждый, кто хочет быстро им оперировать — вынужден строить какие индексы. Эти индексы долго строятся, живут отдельно от самого кода, переизобретаются каждым инструментом. Мы сами создали себе проблему, которую пытаемся героически решить. В этом месте мне вспомнился недавний доклад о структурном логировании, рассказывающий о подобной же проблеме: мы придумали писать логи в текстовые форматы, чтобы потом придумать отдельные программы вроде Logstash для парсинга этих текстовых форматов с целью извлечения из них ранее записанной информации (при этом, конечно, и пишется не всё и извлекается не всё, и контексты теряются, и рассинхронизация на каждом шагу). В общем, интересный доклад, посмотрите. Ну так вот, для логов до понимания масштабов беды мы уже дошли, а до самого кода — ещё, получается, нет.

Длительное время компиляции
Посмотрите на первый попавшийся язык программирования и попробуйте угадать, какую фичу хотят его пользователи. Как в том анекдоте про »4 ствола и всё небо в попугаях» можно пальнуть наугад и окажется, что «неплохо бы ускорить время компиляции\интерпретации» — вот из свеженького, просьбы к Яндексу (а почему не в Спортлото?) улучшить С++. Это же надо, оказывается, что компилятор\интерпретатор ничего не делают в том время, когда вы задорно молотите по клавиатуре (а вы это частенько делаете) и лишь время от времени, проснувшись от летаргического сна командой запуска сборки или требованием интерпретации скрипта они съедают всю память и все ядра CPU, чтобы выполнить свою работу. И, прямо как у студента, ничего не делавшего весь семестр, внезапно оказывается, что нужно напрягаться, тратить много времени и сил. А чего же ты, спрашивается, раньше ничего не делал? Если бы формат сохранения кода предполагал перевод написанного текста программы в некоторую промежуточную форму (что-то чуть-чуть большее, чем поток лексем, но чуть-чуть меньшее, чем готовый бинарный код), то и компиляция была бы значительно более быстрой.

Отсутствие кросс-языковых инструментов разработки
Из-за того, что каждый язык программирования, каждый фреймворк и каждая ОС — это монастырь со своим уставом, у нас совершенно нет никаких инструментов удобной координации написания различных частей системы. Нет, IDE, конечно, всякие есть. Но покажите-ка мне дебаггер, который даст возможность пройти совершенно типичный нынче процесс «скрипт на Python запустил программу на C++, которая дёрнула по сети микросервис на Go, который полез в базу, где дёрнул пару хранимок, после чего это всё вернулось скрипту на Python». И вот чтобы дебаггер дал мне всё — точки останова на любом уровне от скрипта до хранимки, стек вызовов, всё окружение. Чтоб понимал, что вот параметр передался через командную строку, а вот тут он же — сериализировался в REST-запрос, а тут вернулся вот таким образом. Есть такое? Нет такого. Потому, что код — это буковки. Там такие, а здесь другие. Там их один компилятор собрал, здесь — другой. А как-то их связать — сложно это. А вот если бы код был кодом, и там кодом (с функциями, параметрами, циклами, условиями) и тут тоже кодом, то между ними можно было бы пробрасывать мостики, а затем по этим мостикам удобно ходить.

Так доколе?

© Habrahabr.ru