Если есть что-то, чему меня научило функциональное программирование, так это важность ценностей.

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

В целом, серверные службы должны:

  • получать значения из мобильного приложения. например, полезная нагрузка JSON в конечной точке REST
  • выполнять чистую логику над значениями. например, применить f(баланс, покупка)new_balance
  • сохранять значения в базе данных
  • и так далее

Все остальное накладно. Геттеры и сеттеры, Банда четырех паттернов и еще много чего. Конечно, эти абстракции могут позволить вам масштабировать код или размер команды. Но они все еще накладные. Они не являются ни частью бизнес-модели нашего стартапа, ни причиной, по которой генеральный директор решил нанять нас.

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

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

  • входной барьер
  • нет безопасности типов

Возьмем для примера конфиг Kubernetes:

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

Под отсутствием безопасности типов я подразумеваю все преимущества, которые мы теряем и которые иначе были бы в IDE для строго типизированного языка. Автозаполнение, Intellisense и ссылки на ранее объявленные значения.

Для Kubernetes держу пари, есть 3761 инструмент, которые сделают вашу жизнь проще. Но как насчет DSL, которые мы создаем?

Для иллюстрации предположим, что наша задача — создать DSL/конфигурационный файл для интерфейсного интерфейса, управляемого серверной частью. т. е. мы хотим разработать систему, позволяющую создавать интерфейсные приложения с экранами и потоками на основе ответов серверной части. Spotify делает это, кстати.

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

Создание пользовательского интерфейса редактора

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

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

Но у этого есть некоторые минусы:

  • дорого писать - по времени по крайней мере
  • требуется команда с полным стеком
  • это не решает проблему с код-ревью — в случае, если UI выдает такой файл, как файл Kubernetes, код-ревью вообще ограничивается LGTM или 🙈, как в я доверяю что вы использовали пользовательский интерфейс правильно, и я уверен, что пользовательский интерфейс правильный.

Использование наших IDE

Что, если мы напишем, например, плагин для VSCode, который каким-то образом поможет другим разработчикам писать наши DSL? Но подождите, мы уже используем популярный формат, такой как JSON или YAML. Вместо этого мы могли бы написать плагин, который помогает пользователям писать JSON в соответствии с некоторыми соглашениями.

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

Оказывается, решение действительно существует. Первая страница поиска Google напечатала YAML, привела меня к strictyaml, которая привела меня к JSON Schema.

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

В интернете много статей о преимуществах и возможностях JSON Schema, поэтому не буду утомлять вас подробностями. Я просто продемонстрирую это с помощью GIF:

Схема, использованная в этом демо, была извлечена из react-jsonschema-form.

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

Есть способы распространить это и на YAML.

О, одно предложение: не пишите свою схему JSON и свои структуры/классы кода вручную. Пусть одно порождает другое, чтобы был единый источник истины.

Использование данных как кода (как данных)

Преимущество наличия DSL внутри файла JSON заключается в том, что в целом его можно анализировать во время выполнения: нам не нужно развертывать новый экземпляр сервера и останавливать предыдущий. Мы можем curl -d "@config.json" -XPOST localhost:3000/apply обновить поведение приложения.

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

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

В Котлине было бы просто придумать такой файл:

Это именно то, что делает Anko, например, для создания пользовательского интерфейса Android.

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

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