Что не так с OpenAPI?

в 18:26, , рубрики: api, documentation, Microservices, rest api, RESTful

Как мы боролись с документированием API на наших проектах, и как мы немного сошли с ума

У вас на проекте порядок с документацией на API? Скорее всего нет. И в нашей компании порядка не было.

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

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

Например, OpenAPI — самый распространённый язык описания REST API. Писать на этом языке настолько больно, что разработчик никогда не упустит шанс этого не делать (вот хороший пример отношения к OpenAPI).

Возьмём простейший API с одним ендпоинтом:

GET /users/{id}

Пусть этот ендпоинт возвращает ответ с кодом 200 такого вида:

{
  "id": 123, 
  "name": "Tom"
}

В этом случае программист вынужден написать на языке OpenAPI 23 строки (!):

openapi: 3.0.1
paths:
  /users/{id}:
    get:
      parameters:
      - name: id
        in: path
        required: true
        schema: {}
      responses:
        200:
          content:
            application/json:
              schema:
                required: [id, name]
                type: object
                properties:
                  id:
                    type: integer
                    example: 123
                  name:
                    type: string
                    example: "Tom"

Реальные API частенько занимают не 23 строки, а несколько тысяч строк (вот, для примера, API Твиттера на 13 тысяч строк).

Существуют даже специальные инструменты — визуальные редакторы OpenAPI, которые позволяют писать OpenAPI-код не руками, а с помощью графического пользовательского интерфейса (например, https://stoplight.io/). Правда, эти инструменты не слишком помогают: вместо того, чтобы набирать много строк OpenAPI-кода, теперь нужно много кликать мышкой.

В результате, в реальных проектах происходит следующее (приводим усреднённые данные из нескольких исследований):

  • Примерно в половине проектов API не описывают вообще.

  • Примерно в 25% проектов API описывают вручную (т. н. подход “Spec First”). При этом во многих проектах написанную однажды документацию в актуальном состоянии не поддерживают.

  • Примерно в 25% проектов документацию генерируют из программного кода (т. н. подход “Code First”).

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

  1. Во многих (особенно, в крупных) проектах, в проектировании API, помимо разработчиков, участвуют ещё несколько человек: архитекторы, системные аналитики, QA-инженеры, заинтересованные лица из других команд. На таком совещании запросто могут присутствовать 10 человек, и из них далеко не все умеют или хотят генерировать OpenAPI-документы из программного кода. Обычно приходится писать OpenAPI-фалы вручную, обмениваться ими, комментировать, вносить правки и т. д.

  2. Во многих (особенно, в крупных) проектах, используются устаревшие языки или фреймворки, для которых просто не существует генераторов OpenAPI-спецификаций. В итоге, часть API оказывается без документации (и как правило это очень важные API, работающие в компании уже много лет!).

  3. Генераторы OpenAPI-спецификаций нередко содержат ошибки, которые порой не позволяют сгенерировать то, что нужно (вот пример бага в генераторе). Особенно это проявляется в крупных долгоживущих проектах, когда разные части системы, как правило, написаны на разных языках и разных фреймворках. Для каждого фреймворка используется свой генератор OpenAPI, и вероятность того, что один из этих генераторов даст сбой, повышается. Если генератор OpenAPI ломается, то этот API остаётся без документации. Вручную документировать этот API, конечно, никто не будет.

  4. OpenAPI-генераторы способны сгенерировать только часть документации. Но ведь надо еще добавить подробные описания, примеры использования, примеры сообщений (особенно это важно в крупных проектах). Всё это нужно делать вручную и каким-то образом заталкивать в программный код, чтобы потом, на его основе, генератор создал полноценную спецификацию.

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

Подведём итог (цифры даны примерные):

  • Более 50% проектов страдают от отсутствующей или устаревшей документации API.

  • В 25% проектов сотрудники страдают от необходимости писать OpenAPI-файлы вручную (в основном это крупные проекты).

  • В 25% проектов проблема решена с помощью генераторов. Однако, это решение обладает недостатками и ограничениями, которые не позволяют использовать его в остальных 75% проектов.

Это удивительно, насколько серьёзные последствия происходят из такой, казалось бы, незначительной вещи, как «неудобный язык описания API»! Из-за этой «мелочи» разработчики упорно саботируют документирование API, в результате чего огромное число существующих API описано плохо, либо не описано вовсе.

Во всём сказанном мы убедились на собственном опыте. За несколько лет мы перепробовали все возможные варианты документирования API: на некоторых проектах мы писали OpenAPI-документы вручную, на некоторых создавали их генераторами, на некоторых отпускали весь процесс на самотёк.

Однажды мы решили, что больше так жить не хотим.

Мы были уверены, что существует какой-то другой, более простой способ описывать API. Однако то, к чему мы в итоге пришли, удивило даже нас.

Если посмотреть, как программисты рассказывают друг другу о структуре API в обычной рабочей обстановке, то вы никогда не увидите, чтобы они рисовали на досках или отправляли друг другу в чате что-то похожее на OpenAPI-спецификации. Вместо этого они рисуют или отправляют друг другу примеры данных, которые нужно отправить в тот или иной ендпоинт (или получить в ответ). То есть переписка выглядит буквально так:

Как разработчики передают друг другу знания об API

Как разработчики передают друг другу знания об API

Показать пример — это самый простой и натуральный способ передачи знаний. Видимо, так устроен наш мозг. Можно долго, подробно и запутанно что-то объяснять, а потом показать пример — и всё сразу встаёт на свои места.

Можно ли использовать пример данных для формального описания схемы данных? Конечно! Нужно только договориться о формальных правилах интерпретации примера данных. В большинстве случаев эти правила считываются вполне интуитивно. Возьмём всё тот же пример данных:

{ 
  "id": 123, 
  "name": "Tom"
}

Этот пример интуитивно считывается нами в виде следующих четырёх требований к структуре данных:

  1. Данные должны быть объектом.

  2. Этот объект должен содержать два свойства "id" и "name".

  3. Значение свойства "id" должно иметь тип "integer".

  4. Значение свойства "name" должно иметь тип "string".

Возникает вопрос: а если захочется добавить к этим требованиям ещё какое-нибудь «хитрое» требование, которое нельзя выразить с помощью примера данных? Например, если мы захотим сказать, что свойство "name" в этом объекте не обязательное? Опять идём к программистам и подсматриваем, как они это делают. В этом случае к примеру данных они добавляют всем привычные комментарии в свободной форме:

Неформальные пометки в примерах данных

Неформальные пометки в примерах данных

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

{ 
  "id": 123, 
  "name": "Tom" // {optional: true}
}

Шикарно!

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

  1. Язык должен быть максимально интуитивно понятным.

  2. Язык должен максимально задействовать уже знакомые разработчикам термины и конструкции.

  3. Язык должен уметь описывать не только REST API, но и все другие популярные виды API (например, JSON-RPC). Это позволит переиспользовать готовые библиотеки типов в разных видах API.

  4. Язык должен быть полностью совместим с языком OpenAPI (чтобы потом не было трудностей с созданием конвертера в OpenAPI и обратно).

Через пару лет работы мы получили довольно симпатичный язык. Вот, например, спецификация приведённого выше API (в самом начале статьи):

JSIGHT 0.3

GET /users/{id}
  200
    {
      "id" : 123,
      "name": "Tom"
    }

Первая строка "JSIGHT 0.3" — это название и версия языка. В остальном всё должно быть понятно без комментариев. Напомним, на OpenAPI то же самое описание заняло 23 строки.

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

  • редактор с подсветкой синтаксиса;

  • генератор HTML-документации (чтобы получился красивый документ);

  • валидатор сообщений (чтобы все входящие и исходящие сообщения проверялись на соответствие спецификации API).

Мы попробовали всё это в трёх наших проектах, и вот что уже можно сказать:

  • Описание API, кажется, больше не приносит страданий. По крайней мере ни один разработчик возвращаться на OpenAPI не пожелал 🙂

  • Ура! На проектах 100% точная, свежая, актуальная документация API (спасибо валидатору сообщений, который заставляет разработчика поправлять спецификацию API, если она хоть немножко устарела и не соответствует реальности).

  • Валидатор легко встроился во все фреймворки, в том числе в legacy-код (мы написали адаптеры для C/C++, Go, PHP, Python, Java, NodeJS, Lua, и с этим пришлось повозиться).

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

  • один из участников (например, аналитик) расшаривает свой экран и открывает редактор;

  • команда вместе обсуждает будущий API, и аналитик сразу же фиксирует эти идеи с помощью нашего языка, так что все участники видят, что получается;

  • в конце совещания аналитик сохраняет эскиз API в облаке (да, мы сделали ещё облако) и рассылает ссылку на этот документ всем участникам в общий чат.

Теперь проектирование API происходит у нас ровно за одно совместное совещание. С OpenAPI такое нельзя даже представить — на нём невозможно создавать быстрые эскизы. Раньше совещания происходили в 2–3 и более итерации, в промежутках между которыми аналитик фиксировал идеи на OpenAPI — так, как он их понял во время предыдущего совещания. После этого он рассылал OpenAPI-файл всем участникам, и (ну конечно же!) оказывалось, что участники друг друга неверно поняли, и нужно собираться снова. Получилось, что новый язык изменил наш привычный бизнес-процесс дизайна API.

И всё-таки.

И всё-таки, мы признаём, что несколько сошли с ума. Придумывать новый язык в наше время — это, прямо, что-то экстраординарное. По крайней мере, для REST API весь мир уже использует язык OpenAPI. Захотят ли люди переходить на что-то новое, если уже привыкли работать на старом?

Непростой вопрос. Вся история IT-индустрии говорит о том, что сложные и машинно-ориентированные технологии обязательно уступают место простым и интуитивно-понятным. Если разработчику больно работать с чем-то, то рано или поздно появится замена. Так когда-то произошёл переход от XML к JSON. Так когда-то SOAP уступил место REST. Так, скорее всего, случится и с OpenAPI. Но когда это случиться? Каким образом это произойдёт? Что придёт на смену?

Может ли наша безумная попытка претендовать на преемника OpenAPI?

Мы искренне верим в это! Но, как говорится, будущее покажет.

Мы надеемся, что эта статья была интересна. Мы затронули сложные, неодназначные вопросы, на которые пока еще не существует общепринятных ответов. Будем рады вашим комментариям и мыслям. Если любытно, можете посмотреть наш код на гитхабе, мы всё выложили в опенсорс.

Автор: Константин Малышев

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js