Re: Нужны ли в API цифровые коды?

Интересный вопрос поднялся тут https://mxjournal.ru/blog/1481.

По сути своей API – задаёт протокол обмена данными между разными программами (программа, предоставляющая API – поставщик, а программа, его использующая – клиент). Поскольку API (не ограничиваясь вебом) всё-таки программный интерфейс при его проектировании нужно ориентироваться на удобство использования со стороны программного клиента.

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

Текстовые форматы:

  • удобны для чтения человеком
  • более требовательны по памяти
  • генерируют больше трафика при передаче
  • сложнее обрабатываются программно

Двоичные форматы:

  • нечитаемы человеком
  • занимают меньше места
  • проще обрабатываются программно

Есть и компромиссные варианты текстовых форматов, ориентированных на машинную обработку (различной степени читаемости человеком) – XML, YAML или используемый в исходном посте JSON.

Что касается интернета в целом и веба в частности, то широкое распространение текстовых протоколов в 21 веке обусловлено в основном историческими причинами.

Когда компьютеры были большими, а оперативная память маленькая, компьютеры были настолько разными, что ни о какой совместимости между ними и речи не шло. И разница была не только в системах команд или порядке байт в машинных словах – сам размер байта был разным (6, 7, 8, 9, 32, 36 бит). Ещё одной особенностью компьютеров тех времён было то, компьютер от пользователя отделяла линия связи и текстовый терминал, где пользователь мог вводить текстовые данные и видеть вывод также в текстовом виде.

Монополия IBM на рынке компьютеров позволила широко распространить 8-битный байт, а затем и адресацию байтами (а не машинными словами). Основной причиной для введения байтовой адресации была как раз сложность обработки текстовых данных, вводимых пользователями. Эту боль можно испытать и сейчас, например попытавшись обрабатывать текст на TigerSHARC (он может адресовать только машинные слова по 32 бита).

Затем появился стандарт ASCII, определивший 128 символов алфавита и ставящий им в соответствие числовое значение от 0 до 127. Восьмой бит поначалу использовался для контроля ошибок при передаче информации, а когда необходимость в этом отпала – для расширения кодов ASCII символами национальных алфавитов и другими “нужными” знаками (и начался encoding hell).

Затем вместо терминалов стали подключать другие компьютеры и так появились первые текстовые протоколы обмена.

Потом появилась ARPANET, электронная почта и UUCP,RFC822, SMTP, MIME и всё заверте…

Так что современный текстовый веб – это наследие отцов-основателей из давних времён.

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

  1. десериализация: входные данные преобразуются во внутреннее представление
  2. обработка: входные данные преобразуются в выходные данные (также во внутреннем преставлении)
  3. сериализация: выходные данные преобразуются в текстовый формат

Два из этих трёх шагов (1 и 3) не имеют непосредственного отношения к выполнению полезной работы. Но случается так, что для реализации этих шагов требуется писать достаточно много кода. В качестве примера недавно на работе я писал программу для конвертирования из двоичного формата в CSV. Структура программы получилась такая:

  1. ~14,5 килобайт (~650 строк) на описание структур и чтение двоичного формата
  2. ~1,2 килобайт (~70 строк) на преобразование и декодирование двоичных значений в строки
  3. ~16,5 килобайт (~900 строк) на запись в CSV

Если бы исходный формат был текстовым, десериализация потребовала бы ещё больше кода. А больше кода – больше ошибок 🙂

Исходный двоичный файл тем временем при конвертации в CSV увеличился в размере почти в четыре раза.

Поэтому для программных интерфейсов имеет смысл в первую очередь рассматривать двоичные форматы, чтобы уменьшить количество ошибок и лишнего кода. Эта идея была доведена до реализации, например в gRPC (HTTP/2, protobuf), однако в нём код сериализации/десериализации генерируется, а не пишется руками.

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

В исходном посте предлагаются три формата статуса на основе JSON.

Первый вариант: "status": "в работе"

Здесь мне не нравится что:

  1. строки могут отдаваться на разных языках (например, в зависимости от заголовка Accept-Language)
  2. разработчик клиентской программы может не владеть языком, на котором написано сообщение, и оно будет для него менее понятным, чем число (в мире больше людей, которые знают арабские числа, чем любой естественный язык)
  3. строить логику обработки ориентируясь на значение строк в общем случае проблемно:
  • не во всех языках есть switch/case для строк (Си – яркий пример неудобства работы со строками) или ассоциативные массивы, в которых ключами будут строки, а значениями – код (lambda или указатели на функции), который обрабатывает этот статус.
  • строки могут различаться кодировками, и даже использование символов только ASCII тут не спасает (любая однобайтная кодировка гарантировано не совпадёт с UTF-16 и UTF-32).
  • сравнение строк выполняется дольше, чем сравнение чисел, которые они представляют
  • Строки могут превратиться в нечитаемые без особых на то причин. Например, в JSON значение строки заключено в кавычки. А если значение строки должно содержать символ кавычки? Появляется волшебный символ, за ним стаффинг или экранирование и…
{
    "KEY\\t\/\"": "VALUE\\t\/\r\n\"",
    "0": "\u0000",
    "1": "\u0001",
    "31": "\u001f"
}

приятного чтения!

Второй вариант: "status_code": 4, "status_name": "в работе"

Этот вариант лучше первого, потому что в клиентском коде поле status_name можно просто проигнорировать, и выстроить логику работы на основе значения status_code. Однако выделять два поля одной семантической единицы (статуса) я бы не стал. Судя по синтаксису, статус является одним из полей какой-то более крупной сущности (допустим ответа) и два поля, относящиеся к статусу без причины загрязняют структуру ответа. JSON позволяет объединять эти два поля в одну структуру – и грех этим не воспользоваться!

Третий вариант: "status": { "code": 4, "name": "в работе" }

Я хотел бы видеть в вебе API с такими статусами – они и программно обрабатываются легко (даже на чистом C), и расшифровка есть прямо в ответе. Её можно и при разработке посмотреть, и пользователю показать.

Добавить комментарий

Ваш адрес email не будет опубликован.