You've already forked The-API-Book
mirror of
https://github.com/twirl/The-API-Book.git
synced 2025-07-12 22:50:21 +02:00
Merge pull request #51 from Maximys/bugfix/fix-typos
Исправление некоторого количества опечаток
This commit is contained in:
@ -80,7 +80,7 @@ GET /v1/orders/pending
|
||||
* партнёров интересуют только наиболее свежие изменения;
|
||||
* или обработка событий требует последовательного исполнения и не подразумевает параллельности.
|
||||
|
||||
**NB**: третий (и отчасти второй) варианты естественным образом приводят нас к схеме, характерной для клиентских устройств: push-уведомление само по себе не почти содержит полезной информации и только является сигналом для внеочередного поллинга.
|
||||
**NB**: третий (и отчасти второй) варианты естественным образом приводят нас к схеме, характерной для клиентских устройств: push-уведомление само по себе почти не содержит полезной информации и только является сигналом для внеочередного поллинга.
|
||||
|
||||
Применение техник с отправкой только ограниченного набора данных помимо усложнения схемы взаимодействия и увеличения количества запросов имеет ещё один важный недостаток. Если в варианте 1 (сообщение содержит в себе все релевантные данные) мы можем рассчитывать на то, что возврат кода успеха подписчиком эквивалентен успешной обработке сообщения партнёром (что, вообще говоря, тоже не гарантировано, т.к. партнёр может использовать асинхронные схемы), то для вариантов 2 и 3 это заведомо не так: для обработки сообщений партнёр должен выполнить дополнительные действия, начиная с получения нужных данных о заказе. В этом случае нам необходимо иметь раздельные статусы — сообщение доставлено и сообщение обработано; в идеале, второе должно вытекать из логики работы API, т.е. сигналом о том, что сообщение обработано, является какое-то действие, совершаемое партнёром. В нашем кофейном примере это может быть перевод заказа партнёром из статуса `"new"` (заказ создан пользователем) в статус `"accepted"` или `"rejected"` (кофейня партнёра приняла или отклонила заказ). Тогда полный цикл обработки уведомления будет выглядеть так:
|
||||
|
||||
|
@ -197,7 +197,7 @@ DELETE /v1/orders/{id}/items/{item_id}
|
||||
|
||||
#### Разрешение конфликтов совместного редактирования
|
||||
|
||||
Идеи организации изменения состояния ресурса через независимые атомарные идемпотентные операции выглядит достаточно привлекательно и с точки зрения разрешения конфликтов доступа. Так как составляющие ресурса перезаписываются целиком, результатом записи будет именно то, что пользователь видел своими глазами на экране своего устройства, даже если при этом он смотрел на неактуальную версию. Однако этот подход очень мало помогает нам, если мы действительно обеспечить максимально гранулярное изменение данных, как, например, это сделано в онлайн-сервисах совместной работы с документами или системах контроля версий (поскольку для этого нам придётся сделать столь же гранулярные эндпойнты, т.е. буквально адресовать каждый символ документа по отдельности).
|
||||
Идеи организации изменения состояния ресурса через независимые атомарные идемпотентные операции выглядит достаточно привлекательно и с точки зрения разрешения конфликтов доступа. Так как составляющие ресурса перезаписываются целиком, результатом записи будет именно то, что пользователь видел своими глазами на экране своего устройства, даже если при этом он смотрел на неактуальную версию. Однако этот подход очень мало помогает нам, если мы действительно хотим обеспечить максимально гранулярное изменение данных, как, например, это сделано в онлайн-сервисах совместной работы с документами или системах контроля версий (поскольку для этого нам придётся сделать столь же гранулярные эндпойнты, т.е. буквально адресовать каждый символ документа по отдельности).
|
||||
|
||||
Для «настоящего» совместного редактирования необходимо будет разработать отдельный формат описания изменений, который позволит:
|
||||
* иметь максимально возможную гранулярность (т.е. одна операция соответствует одному действию клиента);
|
||||
|
@ -48,7 +48,7 @@ PUT /v1/api-types/{api_type}
|
||||
|
||||
При этом в документации интерфейса мы опишем и тот, и другой эндпойнт. Как несложно заметить, интерфейс `takeout` весьма специфичен. Если запрос бесконтактной доставки мы как-то скрыли за общим `modify`, то на вот такие операции типа подтверждения выдачи нам каждый раз придётся заводить новый метод с уникальным названием. Несложно представить себе, как через несколько итераций интерфейс превратится в свалку из визуально похожих методов, притом формально необязательных — но для подключения своего API нужно будет прочитать документацию каждого и разобраться в том, нужен ли он в конкретной ситуации или нет.
|
||||
|
||||
**NB**: в этом примере мы предполагаем, что наличие эндпойнта `program_takeout_endpoint` является триггером для приложения, которое должно показать кнопку «выдать заказ». Было бы лучше добавить что-то типа поля `supported_flow` в параметры вызова `PUT /api-types/`, чтобы этот флаг задавался явно, а не определялся из неочевидной конвенции. Однако в проблематике замусоривания интерфейсов опциональным методами это ничего не меняет, так что мы опустили эту тонкость ради лаконичности примеров.
|
||||
**NB**: в этом примере мы предполагаем, что наличие эндпойнта `program_takeout_endpoint` является триггером для приложения, которое должно показать кнопку «выдать заказ». Было бы лучше добавить что-то типа поля `supported_flow` в параметры вызова `PUT /api-types/`, чтобы этот флаг задавался явно, а не определялся из неочевидной конвенции. Однако в проблематике замусоривания интерфейсов опциональными методами это ничего не меняет, так что мы опустили эту тонкость ради лаконичности примеров.
|
||||
|
||||
Мы не знаем, правда ли в реальном мире API кофемашин возникнет проблема, подобная описанной. Но мы можем сказать со всей уверенностью, что *всегда*, когда речь идёт об интеграции «железного» уровня, происходят именно те процессы, которые мы описали: меняется нижележащая технология, и вроде бы понятный и ясный API превращается в свалку из legacy-методов, половина из которых не несёт в себе никакого практического смысла в рамках конкретной интеграции. Если мы добавим к проблеме ещё и технический прогресс — представим, например, что со временем все кофейни станут автоматическими — то мы быстро придём к ситуации, когда половина методов *вообще не нужна*, как метод запроса бесконтактной выдачи напитка.
|
||||
|
||||
@ -73,7 +73,7 @@ PUT /v1/api-types/{api_type}
|
||||
registerProgramRunHandler(
|
||||
apiType, (program) => {
|
||||
// Инициализация исполнения заказа
|
||||
// на стороне партерна
|
||||
// на стороне партнёра
|
||||
let execution = initExecution(…);
|
||||
// Подписка на изменения состояния
|
||||
// родительского контекста
|
||||
@ -103,7 +103,7 @@ registerProgramRunHandler(
|
||||
|
||||
Внимательный читатель может возразить нам, что фактически, если мы посмотрим на номенклатуру возникающих сущностей, мы ничего не изменили в постановке задачи, и даже усложнили её:
|
||||
* вместо вызова метода `takeout` мы теперь генерируем пару событий `takeout_requested` / `takeout_ready`;
|
||||
* вместо длинного списка методов, которые необходимо реализовать для интеграции API партнёра, появляются длинные списки полей разных контекстов и событий, которые они генерирует;
|
||||
* вместо длинного списка методов, которые необходимо реализовать для интеграции API партнёра, появляются длинные списки полей разных контекстов и событий, которые они генерируют;
|
||||
* проблема устаревания технологии не меняется, вместо устаревших методов мы теперь имеем устаревшие поля и события.
|
||||
|
||||
Это замечание совершенно верно. Изменение формата API само по себе не решает проблем, связанных с эволюцией функциональности и нижележащей технологии. Формат API решает другую проблему: как оставить при этом партнерский код читаемым и поддерживаемым. Почему в примере с интеграцией через методы код становится нечитаемым? Потому что обе стороны *вынуждены* имплементировать функциональность, которая в их контексте бессмысленна. Код интеграции вендинговых автоматов *должен* ответить «принято» на запрос бесконтактной выдачи — и таким образом, со временем все имплементации будут состоять из множества методов, просто безусловно возвращающих `true` (или `false`).
|
||||
|
@ -3,7 +3,7 @@
|
||||
Попробуем кратко суммировать написанное в трёх предыдущих главах.
|
||||
|
||||
1. Расширение функциональности API производится через абстрагирование: необходимо так переосмыслить номенклатуру сущностей, чтобы существующие методы стали частным (желательно — самым частотным) упрощённым случаем реализации.
|
||||
2. Вышестоящие сущности должны при этом оставаться информационными контекстами для нижестоящих, т.е. не предписывать конкретное поведение, а только сообщать о своём состоянии и предоставлять функциональность для его изменения (прямую через соответствующие методы либо косвенную через получение определённых событий).
|
||||
2. Вышестоящие сущности должны при этом оставаться информационными контекстами для нижестоящих, т.е. не предписывать конкретное поведение, а только сообщать о своём состоянии и предоставлять функциональность для его изменения (напрямую через соответствующие методы либо косвенно через получение определённых событий).
|
||||
3. Конкретная функциональность, т.е. работа непосредственно с «железом», нижележащим API платформы, должна быть делегирована сущностям самого низкого уровня.
|
||||
|
||||
**NB**. В этих правилах нет ничего особенно нового: в них легко опознаются принципы архитектуры SOLID[ref SOLID](https://en.wikipedia.org/wiki/SOLID)[ref:{"short":"Martin, R. C.","extra":["Design Principles and Design Patterns"]}](http://staff.cs.utu.fi/~jounsmed/doos_06/material/DesignPrinciplesAndPatterns.pdf) — что неудивительно, поскольку SOLID концентрируется на контрактно-ориентированном подходе к разработке, а API по определению и есть контракт. Мы лишь добавляем в эти принципы понятие уровней абстракции и информационных контекстов.
|
||||
|
@ -22,7 +22,7 @@
|
||||
* во-первых, люди не запоминают IP-адреса и предпочитают оперировать «говорящими» именами;
|
||||
* во-вторых, IP-адрес является технической сущностью, связанной с узлом сети, а разработчики хотели бы иметь возможность добавлять и изменять узлы, не нарушая работы своих приложений.
|
||||
|
||||
Удобной (и опять же имеющей почти стопроцентное проникновение) абстракцией над IP-адресами оказалась система доменных имён, позволяющий назначить узлам сети человекочитаемые синонимы. Появление доменных имён потребовало разработки клиент-серверных протоколов более высокого, чем TCP/IP, уровня, и для передачи текстовых (гипертекстовых) данных таким протоколом стал HTTP 0.9[ref The Original HTTP as defined in 1991](https://www.w3.org/Protocols/HTTP/AsImplemented.html), разработанный Тимом Бёрнерсом-Ли опубликованный в 1991 году. Помимо поддержки обращения к узлам сети по именам, HTTP также предоставил ещё одну очень удобную абстракцию, а именно назначение собственных адресов эндпойнтам, работающим на одном сетевом узле.
|
||||
Удобной (и опять же имеющей почти стопроцентное проникновение) абстракцией над IP-адресами оказалась система доменных имён, позволяющая назначить узлам сети человекочитаемые синонимы. Появление доменных имён потребовало разработки клиент-серверных протоколов более высокого, чем TCP/IP, уровня, и для передачи текстовых (гипертекстовых) данных таким протоколом стал HTTP 0.9[ref The Original HTTP as defined in 1991](https://www.w3.org/Protocols/HTTP/AsImplemented.html), разработанный Тимом Бёрнерсом-Ли и опубликованный в 1991 году. Помимо поддержки обращения к узлам сети по именам, HTTP также предоставил ещё одну очень удобную абстракцию, а именно назначение собственных адресов эндпойнтам, работающим на одном сетевом узле.
|
||||
|
||||
Протокол был очень прост и всего лишь описывал способ получить документ, открыв TCP/IP соединение с сервером и передав строку вида `GET адрес_документа`. Позднее протокол был дополнен стандартом URL, позволяющим детализировать адрес документа, и далее протокол начал развиваться стремительно: появились новые глаголы помимо `GET`, статусы ответов, заголовки, типы данных и так далее.
|
||||
|
||||
@ -46,15 +46,15 @@ HTTP появился изначально для передачи размеч
|
||||
|
||||
Фактически, на сегодня *идеологически* разница между современным API, следующим классическому архитектурному стилю REST, и современным RPC заключается лишь в разметке кэшируемых данных и принципах адресации: в первом случае единицей доступа является ресурс (а параметры операции передаются дополнительно), а во втором — имя операции (а адреса ресурсов, над которыми она выполняется, передаются дополнительно).
|
||||
|
||||
Мы рассмотрим конкретные технологии разработки таких API в следующей главе, но отметим здесь важный момент: почти все они (за исключением разве что MQTT) работают поверх протокола HTTP. Так что большинство современных RPC-протоколов *одновременно* является HTTP API.
|
||||
Мы рассмотрим конкретные технологии разработки таких API в следующей главе, но отметим здесь важный момент: почти все они (за исключением разве что MQTT) работают поверх протокола HTTP. Так что большинство современных RPC-протоколов *одновременно* являются HTTP API.
|
||||
|
||||
Тем не менее, *обычно* словосочетание «HTTP API» используется не просто в значении «любой API, использующий протокол HTTP»; говоря «HTTP API» мы *скорее* подразумеваем, что он используется не как дополнительный третий протокол транспортного уровня, а именно как протокол уровня приложения, то есть составляющие протокола (такие как: URL, заголовки, HTTP-глаголы, статусы ответа, политики кэширования и т.д.) используются в соответствии с их семантикой, определённой в стандартах. *Обычно* также подразумевается, что в HTTP API использует какой-то из текстовых форматов передачи данных (JSON, XML) для описания вызовов.
|
||||
Тем не менее, *обычно* словосочетание «HTTP API» используется не просто в значении «любой API, использующий протокол HTTP»; говоря «HTTP API» мы *скорее* подразумеваем, что он используется не как дополнительный третий протокол транспортного уровня, а именно как протокол уровня приложения, то есть составляющие протокола (такие как: URL, заголовки, HTTP-глаголы, статусы ответа, политики кэширования и т.д.) используются в соответствии с их семантикой, определённой в стандартах. *Обычно* также подразумевается, что в HTTP API используется какой-то из текстовых форматов передачи данных (JSON, XML) для описания вызовов.
|
||||
|
||||
В рамках настоящего раздела мы поговорим о дизайне сетевых API, обладающих следующими характеристиками:
|
||||
* протоколом взаимодействия является HTTP версий 1.1 и выше;
|
||||
* форматом данных является JSON (за исключением эндпойнтов, специально предназначенных для передачи данных, как правило, файлов, в других форматах);
|
||||
* в качестве идентификаторов ресурсов используется URL в соответствии со стандартом;
|
||||
* семантика вызовов HTTP-эндпойнтов соответствует спецификации;
|
||||
* никакие из веб-стандартов нигде не нарушается специально.
|
||||
* никакие из веб-стандартов нигде не нарушаются специально.
|
||||
|
||||
**Такое API мы будем для краткости называть просто «HTTP API» или «JSON-over-HTTP API»**. Мы понимаем, что такое использование терминологически не полностью корректно, но писать каждый раз «JSON-over-HTTP эндпойнты, утилизирующие семантику, описанную в стандартах HTTP и URL» или «JSON-over-HTTP API, соответствующий архитектурным ограничениям REST» не представляется возможным. Что касается термина «REST API», то как мы покажем дальше, у него нет консистентного определения, поэтому его использования мы также стараемся избегать.
|
||||
|
@ -102,7 +102,7 @@ HTTP-глагол определяет два важных свойства HTTP
|
||||
|
||||
**NB**: распространено мнение, что метод `POST` предназначен только для создания новых ресурсов. Это совершенно не так, создание ресурса только один из вариантов «обработки запроса согласно внутреннему устройству» эндпойнта.
|
||||
|
||||
Важное свойство модифицирующих идемпотентных глаголов — это то, что **URL запроса является его ключом идемпотентности**. `PUT /url` полностью перезаписывает ресурс, заданный своим URL (`/url`), и, таким образом, повтор запроса не изменяет ресурс. Аналогично, повторный вызов `DELETE /url` должен оставить систему в том же состоянии (ресурс `/url` удалён). Учитывая, что метод `GET /url` семантически должен вернуть представление целевого ресурса `/url`, то, если этот метод реализован, он должен возвращать консистентное предыдущим `PUT` / `DELETE` представление. Если ресурс был перезаписан через `PUT /url`, `GET /url` должен вернуть представление, соответствующее переданном в `PUT /url` телу (в случае JSON-over-HTTP API это, как правило, просто означает, что `GET /url` возвращает в точности тот же контент, чтобы передан в `PUT /url`, с точностью до значений полей по умолчанию). `DELETE /url` обязан удалить указанный ресурс — так, что `GET /url` должен вернуть `404` или `410`.
|
||||
Важное свойство модифицирующих идемпотентных глаголов — это то, что **URL запроса является его ключом идемпотентности**. `PUT /url` полностью перезаписывает ресурс, заданный своим URL (`/url`), и, таким образом, повтор запроса не изменяет ресурс. Аналогично, повторный вызов `DELETE /url` должен оставить систему в том же состоянии (ресурс `/url` удалён). Учитывая, что метод `GET /url` семантически должен вернуть представление целевого ресурса `/url`, то, если этот метод реализован, он должен возвращать консистентное предыдущим `PUT` / `DELETE` представление. Если ресурс был перезаписан через `PUT /url`, `GET /url` должен вернуть представление, соответствующее переданному в `PUT /url` телу (в случае JSON-over-HTTP API это, как правило, просто означает, что `GET /url` возвращает в точности тот же контент, что был передан в `PUT /url`, с точностью до значений полей по умолчанию). `DELETE /url` обязан удалить указанный ресурс — так, что `GET /url` должен вернуть `404` или `410`.
|
||||
|
||||
Идемпотентность и симметричность методов `GET` / `PUT` / `DELETE` влечёт за собой нежелательность для `GET` и `DELETE` запросов иметь тело (поскольку этому телу невозможно приписать никакой осмысленной роли). Однако (по-видимому в связи с тем, что многие разработчики попросту не знают семантику этих методов) распространённое ПО веб-серверов обычно разрешает этим методам иметь тело запроса и транслирует его дальше к коду обработки эндпойнта (использование этой практики мы решительно не рекомендуем).
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
4. Насколько строго должна выдерживаться буквальная интерпретация конструкции `ГЛАГОЛ /ресурс`? Если мы принимаем правило «части URL обязаны быть существительными» (и ведь странно применять глагол к глаголу!), то в примерах выше должно быть не `prepare`, а `preparator` или `preparer` (а вариант `/action=prepare&coffee_machine_id=<id>&recipe=lungo` вовсе недопустим, так как нет объекта действия), что, честно говоря, лишь добавляет визуального шума в виде суффиксов «ator», но никак не способствует большей лаконичности и однозначности понимания.
|
||||
|
||||
5. Если сигнатура вызова по умолчанию модифицирующая или неидемпотентная, означает ли это, что операция *обязана* быть модифицирующей / идемпотентной? Двойственность смысловой нагрузки глаголов (семантика vs побочные действия) порождает неопределённость в вопросах организации API. Рассмотрим, например, ресурс `/v1/search`, осуществляющий поиск предложений кофе в нашем учебном API. С каким глаголом мы должны к нему обращаться?
|
||||
5. Если сигнатура вызова по умолчанию модифицирующая или неидемпотентная, означает ли это, что операция *обязана* быть модифицирующей / неидемпотентной? Двойственность смысловой нагрузки глаголов (семантика vs побочные действия) порождает неопределённость в вопросах организации API. Рассмотрим, например, ресурс `/v1/search`, осуществляющий поиск предложений кофе в нашем учебном API. С каким глаголом мы должны к нему обращаться?
|
||||
* С одной стороны, `GET /v1/search?query=<поисковый запрос>` позволяет явно продекларировать, что никаких посторонних эффектов у этого запроса нет (никакие данные не перезаписываются) и результаты его можно кэшировать (при условии, что все значимые параметры передаются в URL).
|
||||
* С другой стороны, согласно семантике операции, `GET /v1/search` должен возвращать *представление ресурса `search`*. Но разве результаты поиска являются представлением ресурса-поисковика? Смысл операции «поиск» гораздо точнее описывается фразой «обработка запроса в соответствии с внутренней семантикой ресурса», т.е. соответствует методу `POST`. Кроме того, можем ли мы вообще говорить о кэшировании поисковых запросов? Страница результатов поиска формируется динамически из множества источников, и повторный запрос с той же поисковой фразой почти наверняка выдаст другой список результатов.
|
||||
|
||||
@ -158,7 +158,7 @@
|
||||
|
||||
#### CRUD-операции в реальной жизни
|
||||
|
||||
Изложенные выше соображения не следует считать критикой концепции CRUD как таковой. Мы лишь указываем, что что в сложных предметных областях «срезание углов» и следование мнемоническим правилам редко работает. Мы начали с двух URL и четырёх-пяти методов манипуляции ими:
|
||||
Изложенные выше соображения не следует считать критикой концепции CRUD как таковой. Мы лишь указываем, что в сложных предметных областях «срезание углов» и следование мнемоническим правилам редко работает. Мы начали с двух URL и четырёх-пяти методов манипуляции ими:
|
||||
* `/v1/orders/` для вызова с глаголом `POST`;
|
||||
* `/v1/orders/{id}` для вызова с глаголами `GET` / `PUT` / `DELETE` / опционально `PATCH`.
|
||||
|
||||
@ -178,4 +178,4 @@
|
||||
* `/v1/orders/search?user_id=<user_id>` для поиска заказов через метод `GET` (простые случаи) или `POST` (если необходимы сложные фильтры);
|
||||
* `PUT /v1/orders/{id}/archive` для архивирования заказа.
|
||||
|
||||
плюс, вероятно, потребуются частные операции типа `POST /v1/orders/{id}/cancel` для проведения атомарных изменений. Именно это произойдёт в реальной жизни: идея CRUD как методологии описания типичных операций над ресурсом с помощью небольшого набора HTTP-глаголов быстро превратится в семейство эндпойнтов, каждый из которых покрывает какой-то аспект жизненного цикла сущности. Это всего лишь означает, что CRUD-мнемоника даёт только стартовый набор гипотез; любая конкретная предметная область требует вдумчивого подхода и дизайна подходящего API. Если же перед вами стоит задача разработать «универсальный» интерфейс, который подходит для работы с любыми сущностями, лучше сразу начинайте с номенклатуры в 10 методов типа описанной выше.
|
||||
плюс, вероятно, потребуются частные операции типа `POST /v1/orders/{id}/cancel` для проведения атомарных изменений. Именно это произойдёт в реальной жизни: идея CRUD как методологии описания типичных операций над ресурсом с помощью небольшого набора HTTP-глаголов быстро превратится в семейство эндпойнтов, каждый из которых покрывает какой-то аспект жизненного цикла сущности. Это всего лишь означает, что CRUD-мнемоника даёт только стартовый набор гипотез; любая конкретная предметная область требует вдумчивого подхода и дизайна подходящего API. Если же перед вами стоит задача разработать «универсальный» интерфейс, который подходит для работы с любыми сущностями, лучше сразу начинайте с номенклатуры в 10 методов, подобной описанной выше.
|
||||
|
@ -38,4 +38,4 @@
|
||||
|
||||
и заложите защиту от этих векторов атак на уровне вашего серверного ПО. Организация OWASP предоставляет хороший обзор лучших security-практик для HTTP API[ref REST Security Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/REST_Security_Cheat_Sheet.html).
|
||||
|
||||
В заключение хотелось бы сказать следующее: HTTP API — это способ организовать ваше API так, чтобы полагаться на понимание семантики операций как разнообразным программным обеспечением, от клиентских фреймворков до серверных гейтвеев, так и разработчиком, который читает спецификацию. В этом смысле экосистема HTTP предоставляет пожалуй что наиболее широкий (и в плане глубины, и в плане распространённости) по сравнению с другими технологиями словарь для описания самых разнообразных ситуаций, возникающих во время работы клиент-серверных приложений. Разумеется, эта технология не лишена своих недостатков, но для разработчика *публичного* API она является выбором по умолчанию — на сегодняшний день скорее надо обосновывать отказ от HTTP API чем выбор в его пользу.
|
||||
В заключение хотелось бы сказать следующее: HTTP API — это способ организовать ваше API так, чтобы полагаться на понимание семантики операций как разнообразным программным обеспечением, от клиентских фреймворков до серверных гейтвеев, так и разработчиком, который читает спецификацию. В этом смысле экосистема HTTP предоставляет, пожалуй, наиболее широкий (и в плане глубины, и в плане распространённости) по сравнению с другими технологиями словарь для описания самых разнообразных ситуаций, возникающих во время работы клиент-серверных приложений. Разумеется, эта технология не лишена своих недостатков, но для разработчика *публичного* API она является выбором по умолчанию — на сегодняшний день скорее надо обосновывать отказ от HTTP API чем выбор в его пользу.
|
@ -16,8 +16,8 @@
|
||||
|
||||
#### Выбор фреймворка для разработки UI-компонентов
|
||||
|
||||
Поскольку UI — высокоуровневая абстракция над примитивами ОС, почти для всех платформ существуют специализированные фреймворки для разработки визуальных компонентов. Выбор такого фреймворка, увы, может быть непростым занятием. Например, в случае веб-платформы её низкоуровневость и популярность привели к тому, что на сегодня количество конкурирующих технологий, предоставляющих фреймворки для разработки SDK, превосходит всякое воображение. Можно упомянуть наиболее распространённые на сегодня React[ref React](https://react.dev/), Angular[ref Angular](https://angular.io/), Svelte[ref Svelte](https://svelte.dev/), Vue.js[ref Vue.js](https://vuejs.org/) и не сдающие своих позиций Bootstrap[ref Bootstrap](https://getbootstrap.com/) и Ember[ref Ember](https://emberjs.com/). Из перечисленных технологий наибольшее проникновение имеет React, но оно всё ещё измеряется в единицах процентов[ref How Many Websites Use React in 2023? (Usage Statistics)](https://increditools.com/react-usage-statistics/). При этом компоненты на «чистом» JavaScript/CSS при этом часто критикуются как неудобные к использованию в перечисленных фреймворках, так как каждый из них реализует весьма строгие методологии. Примерно такая же ситуация наблюдается, например, с разработкой визуальных библиотек для Windows. Вопрос «на каком фреймворке разрабатывать UI-компоненты для этих платформ», увы, не имеет простого ответа — фактически, вам придётся измерять рынки и принимать решения по каждому фреймворку отдельно.
|
||||
Поскольку UI — высокоуровневая абстракция над примитивами ОС, почти для всех платформ существуют специализированные фреймворки для разработки визуальных компонентов. Выбор такого фреймворка, увы, может быть непростым занятием. Например, в случае веб-платформы её низкоуровневость и популярность привели к тому, что на сегодня количество конкурирующих технологий, предоставляющих фреймворки для разработки SDK, превосходит всякое воображение. Можно упомянуть наиболее распространённые на сегодня React[ref React](https://react.dev/), Angular[ref Angular](https://angular.io/), Svelte[ref Svelte](https://svelte.dev/), Vue.js[ref Vue.js](https://vuejs.org/) и не сдающие своих позиций Bootstrap[ref Bootstrap](https://getbootstrap.com/) и Ember[ref Ember](https://emberjs.com/). Из перечисленных технологий наибольшее проникновение имеет React, но оно всё ещё измеряется в единицах процентов[ref How Many Websites Use React in 2023? (Usage Statistics)](https://increditools.com/react-usage-statistics/). При этом компоненты на «чистом» JavaScript/CSS часто критикуются как неудобные к использованию в перечисленных фреймворках, так как каждый из них реализует весьма строгие методологии. Примерно такая же ситуация наблюдается, например, с разработкой визуальных библиотек для Windows. Вопрос «на каком фреймворке разрабатывать UI-компоненты для этих платформ», увы, не имеет простого ответа — фактически, вам придётся измерять рынки и принимать решения по каждому фреймворку отдельно.
|
||||
|
||||
Лучше дела обстоят с актуальными мобильными платформами (а также MacOS), которые гораздо более гомогенны. Однако здесь возникает другая проблема — современные приложения, как правило, поддерживают сразу несколько таких платформ, что приводит к дублированию кода (и номенклатуры API).
|
||||
|
||||
Решением этой проблемы может быть использование кросс-платформенных мобильных (React Native[ref React Native](https://reactnative.dev/), Flutter[ref Flutter](https://flutter.dev/), Xamarin[ref Xamarin](https://dotnet.microsoft.com/en-us/apps/xamarin)) и десктопных (JavaFX[ref JavaFX](https://openjfx.io/), QT[ref QT](https://www.qt.io/)) фреймворков, а также узкоспециализированных решений для конкретных задач (например, Unity[ref Unity](https://docs.unity3d.com/Manual/index.html) для разработки игр). Несомненным преимуществом таких технологий является скорость разработки и универсальность (как кода, так и программистов). Недостатки также достаточно очевидны — от таких приложений может быть сложно добиться оптимальной производительности, и к ним часто неприменимы многие стандартные инструменты, доступные для конкретной платформы; например, отладка и профайлинг могут быть затруднены. На сегодня скорее наблюдается паритет между двумя этими подходами (несколько фактически независимых приложений, написанных на поддерживаемых платформой языках vs. одно кросс-плафторменное приложение).
|
||||
Решением этой проблемы может быть использование кросс-платформенных мобильных (React Native[ref React Native](https://reactnative.dev/), Flutter[ref Flutter](https://flutter.dev/), Xamarin[ref Xamarin](https://dotnet.microsoft.com/en-us/apps/xamarin)) и десктопных (JavaFX[ref JavaFX](https://openjfx.io/), Qt[ref Qt](https://www.qt.io/)) фреймворков, а также узкоспециализированных решений для конкретных задач (например, Unity[ref Unity](https://docs.unity3d.com/Manual/index.html) для разработки игр). Несомненным преимуществом таких технологий является скорость разработки и универсальность (как кода, так и программистов). Недостатки также достаточно очевидны — от таких приложений может быть сложно добиться оптимальной производительности, и к ним часто неприменимы многие стандартные инструменты, доступные для конкретной платформы; например, отладка и профайлинг могут быть затруднены. На сегодня скорее наблюдается паритет между двумя этими подходами (несколько фактически независимых приложений, написанных на поддерживаемых платформой языках vs. одно кросс-плафторменное приложение).
|
@ -3,6 +3,7 @@
|
||||
Первый вопрос об SDK (напомним, так мы будем называть нативную клиентскую библиотеку, предоставляющую доступ к technology-agnostic клиент-серверному API), который мы должны прояснить — почему вообще такое явление как SDK существует. Иными словами, почему использование обёртки для фронтенд-разработчика является более удобным, нежели работа с нижележащим API напрямую.
|
||||
|
||||
Некоторые причины лежат на поверхности:
|
||||
|
||||
1. Протоколы клиент-серверных API, как правило, разрабатываются так, что не зависят от конкретного языка программирования и, таким образом, без дополнительных действий полученные из API данные будут представлены в не самом удобном формате. Например в JSON нет типа данных «дата и время», и его приходится передавать в виде строки; или, скажем, поддержка (де)сериализации хэш-таблиц в протоколах общего назначения отсутствует.
|
||||
|
||||
2. Большинство языков программирования императивные (и чаще всего — объектно-ориентированные), в то время как большинство форматов данных — декларативные. Работать с сырыми данными, полученными из API, таким образом почти всегда неудобно с точки зрения написания кода, программистам зачастую было бы удобнее работать с полученными из API данными как с объектами.
|
||||
|
@ -37,7 +37,7 @@
|
||||
Предположим, что мы хотим разрешить разработчику подставить в наш `SearchBox` свой поисковый запрос — например, чтобы дать возможность разместить в приложении баннер «найти лунго рядом со мной», по нажатию на который происходит переход к нашему компоненту с введённым запросом «лунго». Для этого разработчику потребуется программно показать соответствующий экран и вызвать нужный метод `SearchBox`-а — допустим, не мудрствуя лукаво, мы назовём его просто `search`.
|
||||
|
||||
Два наших метода `search` («чистый» клиент-серверный и компонентный `SearchBox.search`) принимают одни и те же параметры и выдают один и тот же результат. Но *ведут себя* эти методы совершенно по-разному:
|
||||
* если вызвать несколько раз `SearchBox.search`, не дожидаясь ответа сервера, то все запросы, кроме последнего во времени, должны быть проигнорированы; даже если ответы пришли вразнобой, только тот из них, который соответствует новейшему запросу, должен быть показан в UI;
|
||||
* если вызвать несколько раз `SearchBox.search`, не дожидаясь ответа сервера, то все запросы, кроме последнего во времени, должны быть проигнорированы; даже если ответы пришли вразнобой, только тот из них, который соответствует последнему запросу, должен быть показан в UI;
|
||||
* дополнительная задача — что должен вернуть вызов метода `SearchBox.search`, если он был прерван выполнением другого запроса? Если неуспех, то в чём состоит ошибка вызывающего? Если успех, то почему результат не был отражён в UI?
|
||||
* что порождает другую проблему: а если в момент вызова `SearchBox.search` уже исполнялся какой-то запрос, инициированный пользователем — *что должно произойти*? Какой из вызовов приоритетнее — выполненный разработчиком или выполненный самим пользователем?
|
||||
|
||||
|
@ -21,7 +21,7 @@ class SearchBoxComposer
|
||||
}
|
||||
```
|
||||
|
||||
Кроме того, мы можем убрать `Composer` из цепочки подготовки данных так так, чтобы подчинённые компоненты напрямую получали нужные поля напрямую из `SearchBox`:
|
||||
Кроме того, мы можем убрать `Composer` из цепочки подготовки данных так, чтобы подчинённые компоненты напрямую получали нужные поля напрямую из `SearchBox`:
|
||||
|
||||
```typescript
|
||||
class OfferListComponent
|
||||
@ -47,7 +47,7 @@ class OfferListComponent
|
||||
}
|
||||
```
|
||||
|
||||
Тем самым мы утратили возможность подготавливать данные для их показа в списке и, тем самым, возможность встраивать их в любого родителя через соответствующий `Composer`. Но при этом мы сохранили возможность свободно использовать альтернативные реализации компонентов панели, поскольку реакция на взаимодействие пользователя с интерфейсом находится всё ещё находится под контролем `Composer`-а. Как бонус мы получили отсутствие двусторонних взаимодействий между тремя нашими сущностями:
|
||||
Тем самым мы утратили возможность подготавливать данные для их показа в списке и, тем самым, возможность встраивать их в любого родителя через соответствующий `Composer`. Но при этом мы сохранили возможность свободно использовать альтернативные реализации компонентов панели, поскольку реакция на взаимодействие пользователя с интерфейсом всё ещё находится под контролем `Composer`-а. Как бонус мы получили отсутствие двусторонних взаимодействий между тремя нашими сущностями:
|
||||
|
||||
* субкомпоненты *читают* состояние `SearchBox`-а, но не модифицируют его;
|
||||
|
||||
@ -97,4 +97,4 @@ SDK, реализованный в MV*-парадигмах, в теории п
|
||||
|
||||
Предложенная Фаулером парадигма во многом схожа с концепцией `Composer`-а, которую мы обсуждали в предыдущей главе, но с одним заметным различием. По мысли Фаулера собственного состояния у presenter-а нет (за исключением, возможно, кэшей и замыканий), он только вычисляет данные, необходимые для показа визуального интерфейса, из данных модели. Если необходимо манипулировать каким-то низкоуровневым свойством, например, цветом текста в интерфейсе, то нужно разработать модель так, чтобы цвет текста вычислялся presenter-ом из какого-то высокоуровневого поля в модели (возможно, искусственно введённого), что ограничивает возможности альтернативных имплементаций субкомпонентов.
|
||||
|
||||
**NB**: на всякий случай уточним, что автор этой книги не предлагает `Composer` как альтернативную MV*-методологию. Идея предыдущей главы состоит в том, что сложные сценарии декомпозиции UI-компонентов решаются *только* искусственным введением мостиков-уровней абстракции. Неважно, как мы этот мостик назовём и какие правила для него придумаем.
|
||||
**NB**: на всякий случай уточним, что автор этой книги не предлагает `Composer` как альтернативную MV*-методологию. Идея предыдущей главы состоит в том, что сложные сценарии декомпозиции UI-компонентов решаются *только* искусственным введением дополнительных мостиков-уровней абстракции. Неважно, как мы этот мостик назовём и какие правила для него придумаем.
|
@ -51,7 +51,7 @@ class SearchBox {
|
||||
|
||||
* разработать серверный код управления UI ничуть не проще, нежели клиентский;
|
||||
|
||||
* современные клиентские устройства предоставляют широкий спектр функциональности, доступной только клиентскому разработчику, от кэшей до анимаций;
|
||||
* современные клиентские устройства предоставляют широкий спектр функциональности, доступной только клиентскому разработчику, от кэшей до анимаций:
|
||||
|
||||
* гибридный код, который частично получает состояние с сервера, частично обогащает его на клиенте, писать гораздо сложнее, чем чистый клиентский (в силу того, что в Backend-Driven UI содержимое ответа сервера для клиента представляет собой «чёрный ящик», для взаимодействия с которым приходится изобретать дополнительные протоколы);
|
||||
|
||||
|
@ -62,7 +62,7 @@ class SearchBoxComposer {
|
||||
* этот код просто плохо читается: совершенно непонятно, почему события загрузки данных на одном компоненте влияют на пользовательскую функциональность в другом компоненте;
|
||||
* если при загрузке произойдёт какая-то ошибка, событие `endDataLoad` не произойдёт, и интерфейс останется заблокированным навсегда.
|
||||
|
||||
Если вы внимательно читали предыдущие главы, решение этих двух проблем должен быть очевидным. Необходимо абстрагироваться от самого факта загрузки данных и переформулировать проблемы в высокоуровневых терминах. У нас есть разделяемый ресурс — место на экране. Мы можем показывать в один момент времени только одно предложение. Следовательно, если какому-то актору требуется длящийся доступ к панели, он должен этот доступ явно получить. Отсюда следует, что:
|
||||
Если вы внимательно читали предыдущие главы, решение этих двух проблем должно быть очевидным. Необходимо абстрагироваться от самого факта загрузки данных и переформулировать проблемы в высокоуровневых терминах. У нас есть разделяемый ресурс — место на экране. Мы можем показывать в один момент времени только одно предложение. Следовательно, если какому-то актору требуется длящийся доступ к панели, он должен этот доступ явно получить. Отсюда следует, что:
|
||||
* флаг такого доступа должен именоваться явно (например, `offerFullViewLocked`, а не `isDataLoading`);
|
||||
* флаг контролироваться `Composer`-ом, но никак не самой панелью предложения (ещё и потому, что подготовка данных для показа — также ответственность `Composer`-а).
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
1. API — это *полноценный продукт*, как и другое ПО. Вы «продаёте» его точно так же, и к нему полностью применимы принципы управления продуктом. Весьма сомнительно, что вы сможете качественно развивать API, не изучив потребности аудитории, спрос и предложение, рынок и конкурентов.
|
||||
|
||||
2. API — это *очень специфический продукт*. Вы продаёте возможность некоторые действия выполнять автоматизированно через написание кода, и этот факт накладывает большие ограничения на управление продуктом.
|
||||
2. API — это *очень специфический продукт*. Вы продаёте возможность выполнять некоторые действия автоматизированно через написание кода, и этот факт накладывает большие ограничения на управление продуктом.
|
||||
|
||||
Для того, чтобы успешно развивать API, необходимо уметь отвечать именно на этот вопрос: почему ваши потребители предпочтут выполнять те или иные действия *программно*. Вопрос этот не праздный, поскольку, по опыту автора настоящей книги, именно отсутствие у руководителей продукта и маркетологов экспертизы в области работы с API и есть самая большая проблема развития API.
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
2. API может быть лицензирован под открытой лицензией с определёнными ограничениями, которые могут быть сняты путём покупки расширенной лицензии; это может быть как ограничение функциональности API (например, запрет публикации приложения в соответствующем магазине приложений или невозможность сборки приложения в продакшен-режиме без приобретения лицензии), так и ограничения на использование (например, открытая лицензия может быть «заразной», т.е. требовать распространения написанного поверх платформы кода под той же лицензией, или же использование бесплатного API может быть запрещено для определённых целей).
|
||||
|
||||
3. API может быть бесплатен, но компания-разработчик API может оказывать платные услуги по его консультированию и внедрению или просто продавать расширенную техническую поддержку.
|
||||
3. API может быть бесплатен, но компания-разработчик API может оказывать платные услуги по консультированию и внедрению или просто продавать расширенную техническую поддержку.
|
||||
|
||||
4. Разработка API может спонсироваться явно или неявно владельцем платформы или операционной системы [в нашем кофейном примере — производителем «умных» кофе-машин], который заинтересован в том, чтобы разработчики имели как можно больше удобных инструментов для работы с платформой.
|
||||
|
||||
@ -49,7 +49,7 @@
|
||||
* Любые ресурсы выделяются в первую очередь на поддержку внутренних потребителей. Это означает, что:
|
||||
* планы развития API для партнёров совершенно непрозрачны и бывает что попросту абсурдны; очевидные недоработки не исправляются годами;
|
||||
* техническая поддержка внешних пользователей осуществляется по остаточному принципу.
|
||||
* Разработчики внутренних сервисов часто ломают обратную совместимость или выпускают новые мажорные версии, совершенно не заботясь о последствиях этих действий для внешних партнёрах.
|
||||
* Разработчики внутренних сервисов часто ломают обратную совместимость или выпускают новые мажорные версии, совершенно не заботясь о последствиях этих действий для внешних партнёров.
|
||||
|
||||
Всё это приводит к тому, что наличие внешнего API зачастую работает не в плюс компании, а в минус: фактически, вы предоставляете крайне критически и скептически настроенной аудитории очень плохой продукт. Если у вас нет ресурсов на грамотное развитие API как продукта для внешних пользователей — лучше за него не браться совсем.
|
||||
|
||||
@ -64,7 +64,7 @@
|
||||
В этом случае возможны вариации относительно целевой аудитории саморекламы:
|
||||
* вы можете работать на узнаваемость бренда среди конечных пользователей (размещением своих логотипов и ссылок на сайтах и в приложениях партнёров, использующих API), и даже строить сам бренд таким образом [например, если наш кофейный API будет предоставлять рейтинги кофеен, и мы будем работать на то, чтобы потребитель сам требовал от кофеен указывать рейтинг по версии нашего сервиса];
|
||||
* вы можете работать на распространение знания о себе среди бизнес-аудитории [например, чтобы партнёры, размещающие у себя виджет заказа кофе, заодно и изучили каталог ваших шин];
|
||||
* наконец, вы можете распространять API только и исключительно ради того, чтобы заработать себе репутацию среди разработчиков — ради информирования о других ваших продуктов или ради улучшения вашего имиджа как работодателя для ИТ-специалистов (эту деятельность иногда называют словом «технопиар»).
|
||||
* наконец, вы можете распространять API только и исключительно ради того, чтобы заработать себе репутацию среди разработчиков — ради информирования о других ваших продуктах или ради улучшения вашего имиджа как работодателя для ИТ-специалистов (эту деятельность иногда называют словом «технопиар»).
|
||||
|
||||
Дополнительно в этом разделе можно говорить о формировании комьюнити, т.е. сообщества пользователей или разработчиков, лояльных к продукту. Выгоды от существования таких комьюнити бывают довольно существенны: это и снижение расходов на техническую поддержку, и удобный канал информирования о новых сервисах и релизах, и возможность получить бета-тестеров разрабатываемых продуктов.
|
||||
|
||||
|
@ -21,11 +21,11 @@
|
||||
|
||||
#### Проверка продуктовых гипотез
|
||||
|
||||
Помимо общих сложностей с формированием продуктового видения API есть и более частные сложности с проверкой продуктовых гипотез. «Святой грааль» управления продуктом — создание максимально недорогого с точки зрения затраченных ресурсов minimal viable product (MVP) — обычно недоступен для менеджера API. Дело в том, что вы не можете так просто *проверить* MVP, даже если вам удалось его разработать: для проверки MVP API партнёры должны *написать код* (читай — вложить свои деньги); если по итогам этого эксперимента будет принято решение о бесперспективности продукта, эти деньги окажутся потрачены впустую. Разумеется, партнёры к подобного рода предложениям относятся с некоторым скептицизмом. Таким образом «дешёвый» MVP включает в себя либо компенсацию расходов партнёрам, либо затраты на разработку референсного приложения (т.е. в дополнение к MVP API разрабатывается сразу и MVP приложения, использующего этот API).
|
||||
Помимо общих сложностей с формированием продуктового видения API есть и частные сложности с проверкой продуктовых гипотез. «Святой грааль» управления продуктом — создание максимально недорогого с точки зрения затраченных ресурсов minimal viable product (MVP) — обычно недоступен для менеджера API. Дело в том, что вы не можете так просто *проверить* MVP, даже если вам удалось его разработать: для проверки MVP API партнёры должны *написать код* (читай — вложить свои деньги); если по итогам этого эксперимента будет принято решение о бесперспективности продукта, эти деньги окажутся потрачены впустую. Разумеется, партнёры к подобного рода предложениям относятся с некоторым скептицизмом. Таким образом «дешёвый» MVP включает в себя либо компенсацию расходов партнёрам, либо затраты на разработку референсного приложения (т.е. в дополнение к MVP API разрабатывается сразу и MVP приложения, использующего этот API).
|
||||
|
||||
Частично эту проблему можно решить, если выпустить MVP от имени сторонней компании (например, в виде модуля с открытым кодом, опубликованного от лица разработчика). Однако тогда вы получите проблемы с собственно проверкой гипотезы, так как подобные модули рискуют быть просто оставленными без внимания.
|
||||
|
||||
Другой вариант проверки гипотез — это собственный сервис (или сервисы) компании-разработчика API, если такие есть. Внутренние заказчики обычно более лояльно относятся к трате ресурсов на проверку гипотез, и с ними легче договориться о сворачивании или замораживании MVP. Однако таким образом можно проверить далеко не всякую функциональность — а только то, на которую имеется хоть в каком-то приближении внутренний заказ.
|
||||
Другой вариант проверки гипотез — это собственный сервис (или сервисы) компании-разработчика API, если такие есть. Внутренние заказчики обычно более лояльно относятся к трате ресурсов на проверку гипотез, и с ними легче договориться о сворачивании или замораживании MVP. Однако таким образом можно проверить далеко не всякую функциональность — а только ту, на которую имеется хоть в каком-то приближении внутренний заказ.
|
||||
|
||||
Вообще, концепция “eat your own dogfood” применительно к API означает, что у команды продукта есть какие-то свои тестовые приложения (т.н. “pet project”-ы) поверх API. Учитывая трудоёмкость разработки подобных приложений, имеет смысл поощрять их наличие, например, предоставлением бесплатных квот на API и вычислительные ресурсы членам команды.
|
||||
|
||||
|
@ -54,8 +54,8 @@
|
||||
|
||||
Этот факт напрямую влияет на всё, что мы обсуждали выше (кроме, может быть, Open Source — разработчики-любители редко обращают на него внимание):
|
||||
* Ваши лекции, семинары и выступления на конференциях должны как-то подходить и тем, и другим.
|
||||
* Абсолютное большинство нагрузки на вашу техническую поддержку будет создавать первая категория: начинающим разработчикам гораздо сложнее найти ответы на свои вопросы или разобраться в проблеме самостоятельно, и они будут задавать их вам.
|
||||
* При этом вторая категория потребителей будет гораздо более требовательна к качеству как продукта, так и поддержки, и при этом удовлетворить их запросы будет крайне нетривиально.
|
||||
* Абсолютное большинство нагрузки на вашу техническую поддержку будет создавать вторая категория: начинающим разработчикам гораздо сложнее найти ответы на свои вопросы или разобраться в проблеме самостоятельно, и они будут задавать их вам.
|
||||
* При этом первая категория потребителей будет гораздо более требовательна к качеству как продукта, так и поддержки, и при этом удовлетворить их запросы будет крайне нетривиально.
|
||||
|
||||
Наконец, практически невозможно в рамках одного продукта создать такой API, который одинаково хорошо подходит и начинающим разработчикам, и профессионалам; первым необходима максимальная простота реализации базовых сценариев использования API, вторым — возможность адаптировать использование API под конкретный стек технологий и парадигму разработки, и стоящие перед ними задачи, как правило, требуют глубокой кастомизации. Мы обсудим этот вопрос более подробно в главе [«Линейка сервисов API»](#api-product-range).
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
### [Взаимодействие с бизнес-аудиторией][api-product-business-comms]
|
||||
|
||||
Основные принципы работы с партнёрами несколько парадоксально полностью противоположны принципам взаимодействия с разработчиками:
|
||||
Несколько парадоксально, но основные принципы работы с партнёрами полностью противоположны принципам взаимодействия с разработчиками:
|
||||
* с одной стороны, партнёры гораздо более лояльно и зачастую даже с энтузиазмом относятся к предлагаемым им возможностям, особенно бесплатным;
|
||||
* с другой стороны, донести до бизнес-аудитории смысл вашего предложения несравненно сложнее, чем до разработчиков, так как представителям бизнеса в целом гораздо сложнее объяснить, в чём вообще состоят преимущества API (как концепции) по сравнению с сервисом для конечных пользователей.
|
||||
|
||||
|
@ -23,7 +23,7 @@
|
||||
4. Ещё более упростить работу можно с помощью сервисов, генерирующих код. В таком интерфейсе разработчик выбирает один из представленных шаблонов интеграции, кастомизирует некоторые параметры, и получает на выходе готовый фрагмент кода, который он может вставить в своё приложение (и, возможно, дописать необходимую функциональность с использованием API 1-3 уровней). Подобного рода подход ещё часто называют «программированием мышкой». [В случае нашего кофейного API примером такого сервиса мог бы служить визуальный редактор форм/экранов, в котором пользователь расставляет UI элементы и получает полный код приложения, или консольный скрипт, который генерирует «скелет» приложения.]
|
||||
5. Ещё более упростить такой подход можно, если результатом работы такого сервиса будет уже не код поверх API, а готовый компонент / виджет / фрейм, подключаемый одной строкой. [Например, если мы дадим возможность разработчику вставлять на свой сайт iframe, в котором можно заказать кофе, кастомизированный под нужды заказчика, либо, ещё проще, описать правила формирования «deep link-а»[ref Mobile Deep Linking](https://en.wikipedia.org/wiki/Mobile_deep_linking), который приведёт пользователя на наш сервис.]
|
||||
|
||||
В конечном итоге можно прийти к концепции мета-API, когда готовые визуальные компоненты тоже будут иметь какое-то свой высокоуровневый API, который «под капотом» будет обращаться к базовым API.
|
||||
В конечном итоге можно прийти к концепции мета-API, когда готовые визуальные компоненты тоже будут иметь какой-то свой высокоуровневый API, который «под капотом» будет обращаться к базовым API.
|
||||
|
||||
Важным преимуществом наличия линейки продуктов API является не только возможность адаптировать его к возможностям конкретного разработчика, но и увеличение уровня вашего контроля над кодом, встроенным в приложение партнёра.
|
||||
1. Приложения, использующие физические интерфейсы, полностью вне вашей досягаемости; вы не можете, например, форсировать переход на новые версии технологической платформы или, скажем, добавить в них рекламные размещения.
|
||||
|
@ -30,7 +30,7 @@
|
||||
|
||||
Кратко суммируем вышесказанное:
|
||||
* считать прямые показатели, такие как общее число пользователей и партнёров, — необходимый минимум, без которого сложно двигаться дальше, но это не KPI;
|
||||
* KPI для продукта API должен выставлять исходя из количества целевых действий, которые производятся через платформу;
|
||||
* KPI для продукта API должен формулироваться исходя из количества целевых действий, которые производятся через платформу;
|
||||
* определение «целевых действий» зависит от модели монетизации и может быть как прямолинейным «количество платящих клиентов» или «количество кликов по рекламе», так и достаточно опосредованным и эвристическим типа «количество посетителей блога команды разработки».
|
||||
|
||||
#### SLA
|
||||
@ -47,7 +47,7 @@
|
||||
|
||||
При измерении KPI любого сервиса критически важно измерять не только свои собственные показатели, но и состояние рынка:
|
||||
* какую долю рынка вы занимаете, и как она меняется во времени?
|
||||
* растёт ли ваш сервис быстрее рынка, в темпе рынка или медленнее его?
|
||||
* растёт ли ваш сервис быстрее рынка, в темпе рынка или медленнее него?
|
||||
* какая доля прироста обеспечена ростом самого рынка, а какая — вашими действиями?
|
||||
|
||||
Получить ответы на эти вопросы в случае сервисов API может быть достаточно нетривиально. В самом деле, как выяснить, сколько интеграций за тот же период времени выполнил конкурент, и какое количество целевых действий пользователя выполняется через конкурирующие API? Иногда вам могут помочь с этими данными компании-разработчики аналитических инструментов для приложений, но чаще всего вам нужно будет самостоятельно заниматься мониторингом крупных площадок, которые потенциально могли бы быть интегрированы с вашим API, и отмечать, какие конкурирующие сервисы они используют. Аналогичная ситуация наблюдается и ростом рынков: если ваша ниша недостаточно заметна для того, чтобы крупная независимая аналитическая компания выпустила его исследование, вам придётся или заказать такое исследование за свой счёт, или прикинуть нужные цифры самостоятельно — аналогичным образом, через исследование потенциальных потребителей.
|
||||
Получить ответы на эти вопросы в случае сервисов API может быть достаточно нетривиально. В самом деле, как выяснить, сколько интеграций за тот же период времени выполнил конкурент, и какое количество целевых действий пользователя выполняется через конкурирующие API? Иногда вам могут помочь с этими данными компании-разработчики аналитических инструментов для приложений, но чаще всего вам нужно будет самостоятельно заниматься мониторингом крупных площадок, которые потенциально могли бы быть интегрированы с вашим API, и отмечать, какие конкурирующие сервисы они используют. Аналогичная ситуация наблюдается и с ростом рынков: если ваша ниша недостаточно заметна для того, чтобы крупная независимая аналитическая компания выпустила его исследование, вам придётся или заказать такое исследование за свой счёт, или прикинуть нужные цифры самостоятельно — аналогичным образом, через исследование потенциальных потребителей.
|
@ -35,7 +35,7 @@
|
||||
* примеры должны быть лаконичными и атомарными: смешивание в одном фрагменте кода множества разных трюков из различных предметных областей резко снижает его понятность и применимость;
|
||||
* код примеров должен быть максимально приближен к реальному приложению; автор этой книги сталкивался с ситуацией, когда синтетический фрагмент кода, абсолютно бессмысленный в реальном мире, был бездумно растиражирован разработчиками в огромном количестве.
|
||||
|
||||
В идеале примеры должны быть провязаны со всеми остальными видами документации. В частности, референс должен содержать примеры, релевантные описанию сущностей.
|
||||
В идеале примеры должны быть связаны со всеми остальными видами документации. В частности, референс должен содержать примеры, релевантные описанию сущностей.
|
||||
|
||||
##### Песочницы
|
||||
|
||||
@ -59,13 +59,13 @@
|
||||
* стать точкой входа по умолчанию, максимально понятным и полезным текстом для тех, кто впервые услышал о вашем API;
|
||||
* вовлечь разработчиков, дать им «пощупать» сервис на живом примере.
|
||||
|
||||
Quick start-ы также являются отличным индикатором того, насколько хорошо вы справились с определением частотных кейсов и разработкой хелперных методов. Если ваш quick start содержит более десятка строк кода, вы точно что-то делаете не так.
|
||||
Quick start-ы также являются отличным индикатором того, насколько хорошо вы справились с определением наиболее частых кейсов и разработкой хелперных методов. Если ваш quick start содержит более десятка строк кода, вы точно что-то делаете не так.
|
||||
|
||||
##### Часто задаваемые вопросы и база знаний
|
||||
|
||||
После того, как вы опубликуете API и начнёте поддерживать пользователей (см. предыдущую главу), у вас также появится понимание наиболее частых вопросов пользователей. Если интегрировать ответы на эти вопросы в документацию так просто не получается, имеет смысл завести отдельный раздел с часто задаваемыми вопросами (ЧаВо, англ. FAQ). Раздел ЧаВо должен отвечать следующим критериям:
|
||||
* отвечать на реальные вопросы пользователей
|
||||
* часто можно встретить такие разделы, составленные не по реальным обращениям и отражающие в основном стремление владельца API ещё разок донести какую-то важную информацию; конечно, такой FAQ в лучшем случае бесполезен, а в худшем раздражает; за идеальными примерами реализации этого антипаттерна можно обратиться на сайт любого банка или авиакомпании);
|
||||
* часто можно встретить такие разделы, составленные не по реальным обращениям и отражающие в основном стремление владельца API ещё разок донести какую-то важную информацию; конечно, такой FAQ в лучшем случае бесполезен, а в худшем раздражает (за идеальными примерами реализации этого антипаттерна можно обратиться на сайт любого банка или авиакомпании);
|
||||
* и вопрос, и ответ должны быть сформулированы лаконично и понятно; в ответе допустимо (и даже желательно) дать ссылку на соответствующие разделы руководства и справочника, но сам по себе ответ не должен превышать пары абзацев.
|
||||
|
||||
Кроме того, раздел ЧаВо очень хорошо подходит для того, чтобы явно донести главные преимущества вашего API. В виде пары вопрос-ответ можно наглядно рассказать о том, как ваш API решает сложные проблемы красиво и удобно (или хотя бы решает вообще, в отличие от API конкурентов).
|
||||
|
@ -29,6 +29,6 @@
|
||||
|
||||
#### Автоматизация тестирования
|
||||
|
||||
Ваша конечная цель при разработке тестового API, независимо от выбранного варианта — это позволить партнёрам автоматизировать тестирование их продуктов. Разработку тестовой среды нужно вести именно с этим прицелом; например, если для оплаты заказа необходимо перевести пользователя на страницу 3-D Secure, то в API тестовой среды должен быть предусмотрена возможность программно симулировать прохождение (или непрохождение) пользователем этого вида проверки. Кроме того, в обоих вариантах возможно (и скорее даже желательно) выполнение сценариев в ускоренном масштабе времени, что позволяет производить автоматическое тестирование гораздо быстрее ручного.
|
||||
Ваша конечная цель при разработке тестового API, независимо от выбранного варианта — это позволить партнёрам автоматизировать тестирование их продуктов. Разработку тестовой среды нужно вести именно с этим прицелом; например, если для оплаты заказа необходимо перевести пользователя на страницу 3-D Secure, то в API тестовой среды должна быть предусмотрена возможность программно симулировать прохождение (или непрохождение) пользователем этого вида проверки. Кроме того, в обоих вариантах возможно (и скорее даже желательно) выполнение сценариев в ускоренном масштабе времени, что позволяет производить автоматическое тестирование гораздо быстрее ручного.
|
||||
|
||||
Конечно, далеко не все партнёры этой возможностью смогут воспользоваться (что помимо прочего означает, что «ручной» способ протестировать пользовательские сценарии тоже должен поддерживаться наряду с программным) просто потому что далеко не все бизнесы могут позволить себе нанять автоматизатора тестирования. Тем не менее, сама возможность такие автотесты писать — огромное конкурентное преимущество вашего API в глазах технически продвинутых партнёров.
|
Reference in New Issue
Block a user