image

Наверняка при разработке API не раз появлялись сложности с документацией: то её нет, то она не отображает поведение, описанное в коде.

С точки зрения разработчика, написание документации (одной только внутренней) занимает не меньше времени, чем написание самого кода. Знакомо? Тогда добро пожаловать под кат.

А проблема-то есть?


Наша команда давно разрабатывает API, который является основой нашего продукта, но живых пользователей у него, на тот момент, не было, поэтому и необходимости документировать что-то для внешнего использования никто не видел. Как и все команды, мы начали с внутренней документации — сначала один метод, потом другой. В нашем пространстве в Confluence можно найти десяток другой страниц, где отображена довольно шаблонная информация – что за метод API, какой у него query path, какие параметры и что мы получим на выходе.

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

Мы придумали решение (собственный велосипед), как можно, занимаясь обычной деятельностью разработчика, не задумываться о написании и актуализации внутренней документации.

Немного решений


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

  • Code first, specification next
  • Specification first, code next

Начну со второго, как с варианта, который нам меньше всего подходил.

Specification first, code next — это про генерацию кода, на основе спецификации, то есть кода не существует, пока вы не напишите спецификацию.

Самый простой пример — Swagger Codegen.

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

Code first, specification next — тут всё просто, сначала пишем код, потом спецификацию. Но тут встал вопрос — а если нам не хочется делать лишних движений, чтобы генерировалась спецификация?

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

«Ты ж программист» — сказал я себе и решил написать небольшой прототип, который позволит не писать всю эту рутинную фигню.

Делая очередную задачу и написать n-ый функциональный тест, я понял, что вся информация для спецификации у нас уже есть.

У нас есть функциональные тесты, которые содержат практически всю нужную нам информацию:

  • Что вызывается
  • С чем вызывается (параметры, тело, заголовки и пр.)
  • Какой результат ожидается (статус код, тело ответа)

Почему бы не сделать собственный велосипед?


Практически всё, что мы обычно пишем в спецификации у нас есть. Дело за малым — закодить это дело.

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

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

Проблему, которая была изначально, мы решили. Но с таким решением есть немало рисков:

  • Цена поддержи собственного велосипеда
  • Расширение необходимого функционала
  • Актуализация и синхронизация переводов

Эти риски нужно иметь в виду и, если они начинают срабатывать, то принимать меры.

Комментарии (11)


  1. lowadka
    30.10.2019 21:00

    Проблема очень знакома, пишем долгое время документацию вручную, времени съедает много, часто случается что забыли что-то обновить, в общем не рабочее решение.

    Сейчас разрабатываю rest-api-bundle для Symfony и тоже хочу реализовать там автогенерацию документации swagger. Но я пошёл другим путем, запросы и ответы в бандле описываются в виде классов, а уже по этим классам будет генерироваться json схема.


    1. Chrome
      30.10.2019 21:34
      -1

      Nelmio api doc bundle умеет генерировать документацию из аннотаций и может работать в паре с rest api bundle для симфони . Заодно можно получить sandbox на халяву так сказать. И писать ничего не надо, прямо пишем метод, в аннотациях к нему пишем документацию и эту документацию сразу преобразуем в html используя этот бандл


      1. lowadka
        30.10.2019 22:01

        Спасибо. Оба решения видел, но мне они не зашли, на мой взгляд, можно лучше.


        1. Chrome
          30.10.2019 22:08

          Как ни странно, всегда можно лушче (я ещё ни разу не видел код, который нельзя улучшить), я не касаюсь [sarcasm]фатальных недостатков ПО (типа код написан не нами и код написан не на нашем любимом языке программирования)[/sarcasm]. В данном случае, если не зашли, то можно почерпнуть оттуда сведения чтобы обойти все грабли, на которые люди уже наступили в этих проектах, ну и знание какие варианты вообще есть. А вообще писать что-то своё всегда полезно, хотя бы просто для понимания.


    1. FJCrux Автор
      31.10.2019 08:47

      Эти классы описывают входные и выходные параметры action'ов? То есть вместо request на вход, там какой-то класс, на выход вместо прямой записи в response body там тоже какой-то класс. А как вы, в таком случае, будете обрабатывать ошибочные случаи?


      1. pawnhearts
        31.10.2019 09:54

        Я думал автогенерация документации и всяких swagger`ов — стандартная фича любого rest фреймворка. В любом случае есть же какой-то сериализатор/валидатор входных данных. И сериализатор выходных, надо же как-то данные из бд преобразовывать в json. В них достаточно данных для создания доков плюс докстринги.

        github.com/marshmallow-code/apispec github.com/maximdanilchenko/aiohttp-apispec www.django-rest-framework.org/topics/documenting-your-api (и у каждого поля есть www.django-rest-framework.org/api-guide/fields/#help_text с описанием)


        1. FJCrux Автор
          31.10.2019 10:00

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


      1. lowadka
        31.10.2019 11:06

        Эти классы описывают входные и выходные параметры action'ов

        Все верно, реквест модели выглядят примерно вот так:
        Пример
        <?php
        
        namespace App\AcmeBundle\RequestModel;
        
        use RestApiBundle\Annotation\RequestModel as Mapper;
        use Symfony\Component\Validator\Constraints as Assert;
        use RestApiBundle\RequestModelInterface;
        
        class CreateMovie implements RequestModelInterface
        {
            /**
             * @var string
             *
             * @Mapper\StringType()
             * @Assert\Length(min=3)
             */
            private $name;
        
            /**
             * @var string[]|null
             *
             * @Mapper\CollectionType(type=@Mapper\StringType(), nullable=true)
             */
            private $genres;
            
            ... getters and setters
        }
        


        1. FJCrux Автор
          31.10.2019 11:08

          Про обработку ошибок я скорее в контексте добавления их в спецификацию


          1. lowadka
            31.10.2019 11:12

            Добавление ошибок бизнес логики?

            Они все единого формата, т.е. на них у клиентов универсальный обработчик, соответственно и в документации такое не описываем.

            А если все же на основе какой-то ошибки нужно построить логику – то этот момент пойдет уже в другой тип документации, в которой текстом описано как работать с методами и решать задачи.


            1. FJCrux Автор
              31.10.2019 11:37

              На них вряд ли стоит строить БЛ, но у нас была потребность показать пользователю, какие от данной команды можно ожидать неуспешные результаты