From 14f0518793fa88e77c938113d094ecc5c326ed86 Mon Sep 17 00:00:00 2001 From: Sergey Konstantinov Date: Fri, 20 May 2022 00:36:23 +0300 Subject: [PATCH] Fedor Golubev's feedback addressed --- README.md | 2 +- .../05.md | 2 +- .../07.md | 14 ++++++++++++++ .../drafts/04-Section III. The API Product/02.md | 14 +++++++++++++- .../03-Раздел II. Обратная совместимость/03.md | 2 +- .../03-Раздел II. Обратная совместимость/05.md | 2 +- .../03-Раздел II. Обратная совместимость/07.md | 14 ++++++++++++++ .../drafts/04-Раздел III. API как продукт/02.md | 16 ++++++++++++++-- .../07. KPI API.md | 6 +++++- 9 files changed, 64 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index de4c254..7704fa4 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ I am NOT accepting pull requests introducing any new content, since I'm willing Thanks [art.mari.ka](https://www.instagram.com/art.mari.ka/) for the illustration & inspiration. -Thanks [Ilya Subbotin](https://ru.linkedin.com/in/isubbotin) for the valuable feedback. +Thanks [Ilya Subbotin](https://ru.linkedin.com/in/isubbotin) and [Fedor Golubev](https://www.linkedin.com/in/fedor-golubev-93910b5/) for the valuable feedback. Thanks @tholman for https://github.com/tholman/github-corners. diff --git a/src/en/clean-copy/03-Section II. The Backwards Compatibility/05.md b/src/en/clean-copy/03-Section II. The Backwards Compatibility/05.md index 4fb960c..5ffe602 100644 --- a/src/en/clean-copy/03-Section II. The Backwards Compatibility/05.md +++ b/src/en/clean-copy/03-Section II. The Backwards Compatibility/05.md @@ -48,7 +48,7 @@ PUT /partners/{id}/coffee-machines "format" }, "program_state_endpoint", - "program_stop_endpoint" + "program_cancel_endpoint" } }, …] } diff --git a/src/en/clean-copy/03-Section II. The Backwards Compatibility/07.md b/src/en/clean-copy/03-Section II. The Backwards Compatibility/07.md index 844dea7..7a1cda8 100644 --- a/src/en/clean-copy/03-Section II. The Backwards Compatibility/07.md +++ b/src/en/clean-copy/03-Section II. The Backwards Compatibility/07.md @@ -18,6 +18,20 @@ Any software must be tested, and APIs ain't an exclusion. However, there are som 1. Often the requirements like ‘the `getEntity` function returns the value previously being set by the `setEntity` function’ appear to be too trivial to both developers and QA engineers to have a proper test. But it's quite possible to make a mistake there, and we have actually encountered such bugs several times. 2. The interface abstraction principle must be tested either. In theory, you might have considered each entity as an implementation of some interface; in practice, it might happen that you have forgotten something, and alternative implementations aren't actually possible. For testing purposes, it's highly desirable to have an alternative realization, even a provisional one. +##### Isolate the dependencies + +In the case of a gateway API that provides access to some underlying API or aggregates several APIs behind a single façade, there is a strong temptation to proxy the original interface as is, thus not introducing any changes to it and making a life much simpler by sparing an effort needed to implement the weak-coupled interaction between services. For example, while developing program execution interfaces as described in the [Chapter 9](#chapter-9) we might have taken the existing first-kind coffee-machine API as a role model and provided it in our API by just proxying the requests and responses as is. Doing so is highly undesirable because of several reasons: + * usually, you have no guarantees that the partner will maintain backwards compatibility or at least keep new versions more or less conceptually akin to the older ones; + * nay partner's problems will automatically ricochet into your customers. + +The best practice is quite the opposite: isolate the third-party API usage, e.g. develop an abstraction level that will allow for: + * keeping backwards compatibility intact because of extension capabilities incorporated in the API design; + * negating partner's problems by the technical means: + * limiting the partner's API usage in case of an unpredicted surge in your API usage; + * implementing the retry policies or other methods of recovering after failures; + * caching some data and states to have the ability to provide some (at least partial) functionality even if the partner's API is fully unreachable; + * finally, configuring an automatical fallback to another partner or alternative API. + ##### Implement your API functionality atop of public interfaces There is an antipattern that occurs frequently: API developers use some internal closed implementations of some methods which exist in the public API. It happens because of two reasons: diff --git a/src/en/drafts/04-Section III. The API Product/02.md b/src/en/drafts/04-Section III. The API Product/02.md index 5fe58b6..23d5074 100644 --- a/src/en/drafts/04-Section III. The API Product/02.md +++ b/src/en/drafts/04-Section III. The API Product/02.md @@ -38,7 +38,19 @@ This case is the most interesting one from the API developers' point of view as Access to the API might be unconditionally paid. However, hybrid models are more common: the API is free till some conditions are met, such as usage limits or constraints (for example, only non-commercial projects are allowed). Sometimes the API is provided for free with minimal restrictions to popularize the platform (for example, Apple Maps). -B2B services are a special case. As B2B Service providers benefit from offering diverse capabilities to partners, and conversely partners often require maximum flexibility to cover their specific needs, providing an API might be the optimal solution for both. Large companies have their own IT departments are more frequently need APIs to connect to their internal systems and integrate into business processes. Also, the API provider company itself might play the role of such a B2B customer if its own products are built on top of the API, and external API exists merely as a byproduct of the internal one. +B2B services are a special case. As B2B Service providers benefit from offering diverse capabilities to partners, and conversely partners often require maximum flexibility to cover their specific needs, providing an API might be the optimal solution for both. Large companies have their own IT departments are more frequently need APIs to connect to their internal systems and integrate into business processes. Also, the API provider company itself might play the role of such a B2B customer if its own products are built on top of the API. + +**NB**: we absolutely condemn the practice of providing external API merely as a byproduct of the internal one without any additional product and technical efforts. The main problem of such APIs is that partners' interests are not taken into account, which leads to numerous problems. + * the API doesn't cover integration use-cases well: + * internal customers usually employ quite a specific technological stack, and the API is poorly optimized to work with other programming languages / operating systems / frameworks; + * internal customers are much more familiar with the API concepts; they might take a look at the source code or talk to the API developers directly, so the learning curve is pretty flat for them; + * documentation only covers some subset of use-cases needed by internal customers; + * the API services ecosystem which we will describe in the corresponding chapter later usually doesn't exist. + * Any resources spent are directed to covering internal customer needs first. It means the following: + * API development plans are totally opaque to partners, and sometimes look just absurdly with obvious problems being neglected for years; + * technical support of external customers is financed on leftovers. + +All those problems lead to having an external API that actually hurts the company's reputation, not improves it. In fact, you're providing a very bad service for a very critical and sceptical auditory. If you don't have a resource to develop the API as a product for external customers, better don't even start. ##### API = an advertisment site diff --git a/src/ru/clean-copy/03-Раздел II. Обратная совместимость/03.md b/src/ru/clean-copy/03-Раздел II. Обратная совместимость/03.md index ec7367d..86f2414 100644 --- a/src/ru/clean-copy/03-Раздел II. Обратная совместимость/03.md +++ b/src/ru/clean-copy/03-Раздел II. Обратная совместимость/03.md @@ -2,7 +2,7 @@ В предыдущих разделах мы старались приводить теоретические правила и иллюстрировать их на практических примерах. Однако понимание принципов проектирования API, устойчивого к изменениям, как ничто другое требует прежде всего практики. Знание о том, куда стоит «постелить соломку» — оно во многом «сын ошибок трудных». Нельзя предусмотреть всего — но можно выработать необходимый уровень технической интуиции. -Поэтому в этом разделе мы поступим следующим образом: возьмём наше [модельный API](#chapter-12) из предыдущего раздела, и проверим его на устойчивость в каждой возможной точке — проведём некоторый «вариационный анализ» наших интерфейсов. Ещё более конкретно — к каждой сущности мы подойдём с вопросом «что, если?» — что, если нам потребуется предоставить партнерам возможность написать свою независимую реализацию этого фрагмента логики. +Поэтому в этом разделе мы поступим следующим образом: возьмём наш [модельный API](#chapter-12) из предыдущего раздела, и проверим его на устойчивость в каждой возможной точке — проведём некоторый «вариационный анализ» наших интерфейсов. Ещё более конкретно — к каждой сущности мы подойдём с вопросом «что, если?» — что, если нам потребуется предоставить партнерам возможность написать свою независимую реализацию этого фрагмента логики. **NB**. В рассматриваемых нами примерах мы будем выстраивать интерфейсы так, чтобы связывание разных сущностей происходило динамически в реальном времени; на практике такие интеграции будут делаться на стороне сервера путём написания ad hoc кода и формирования конкретных договорённостей с конкретным клиентом, однако мы для целей обучения специально будем идти более сложным и абстрактным путём. Динамическое связывание в реальном времени применимо скорее к сложным программным конструктам типа API операционных систем или встраиваемых библиотек; приводить обучающие примеры на основе систем подобной сложности было бы, однако, чересчур затруднительно. diff --git a/src/ru/clean-copy/03-Раздел II. Обратная совместимость/05.md b/src/ru/clean-copy/03-Раздел II. Обратная совместимость/05.md index 996c248..0cd2ee9 100644 --- a/src/ru/clean-copy/03-Раздел II. Обратная совместимость/05.md +++ b/src/ru/clean-copy/03-Раздел II. Обратная совместимость/05.md @@ -47,7 +47,7 @@ PUT /v1/api-types/{api_type} "format" }, "program_state_endpoint", - "program_stop_endpoint" + "program_cancel_endpoint" } } ``` diff --git a/src/ru/clean-copy/03-Раздел II. Обратная совместимость/07.md b/src/ru/clean-copy/03-Раздел II. Обратная совместимость/07.md index 0e11369..1bc82cd 100644 --- a/src/ru/clean-copy/03-Раздел II. Обратная совместимость/07.md +++ b/src/ru/clean-copy/03-Раздел II. Обратная совместимость/07.md @@ -18,6 +18,20 @@ 1. Часто требования вида «функция `getEntity` возвращает значение, установленное вызовом функции `setEntity`» кажутся и разработчикам, и QA-инженерам самоочевидными и не проверяются. Между тем допустить ошибку в их реализации очень даже возможно, мы встречались с такими случаями на практике. 2. Принцип абстрагирования интерфейсов тоже необходимо проверять. В теории вы может быть и рассматриваете каждую сущность как конкретную имплементацию абстрактного интерфейса — но на практике может оказаться, что вы чего-то не учли и ваш абстрактный интерфейс на деле невозможен. Для целей тестирования очень желательно иметь пусть условную, но отличную от базовой реализацию каждого интерфейса. +##### Изолируйте зависимости + +В случае, если API является гейтвеем, предоставляющим доступ к какому-то нижележащему API или агрегирующим несколько различных API за одним фасадом, велик соблазн предоставить оригинальный интерфейс as is, не внося в него изменений и не усложняя себя жизнь разработкой слабо связанного взаимодействия. Например, разрабатывая интерфейс для запуска программ, описанный в [главе 9](#chapter-9), мы могли бы взять за основу интерфейс кофе-машин первого типа и предоставить его в виде API, проксируя запросы и ответы как есть. Делать так ни в коем случае нельзя по нескольким причинам: + * как правило, у вас нет никаких гарантий, что партнёр будет поддерживать свой API в обратно-совместимом или хотя бы концептуально похожем виде; + * любые проблемы партнёра будут автоматически отражаться на ваших клиентах. + +Напротив, хорошей практикой будет изолировать использование API третьей стороны, т.е. разработать программную обвязку, которая позволит: + * сохранять обратную совместимость за счёт правильно подобранных точек расширения; + * нивелировать проблемы партнёра техническими средствами: + * ограничивать нагрузку на API партнёра в случае непредвиденного всплеска нагрузки на ваш API; + * реализовывать политики перезапросов и иных способов восстановления после ошибок; + * кэшировать какие-то критичные данные и состояния, чтобы иметь возможность предоставлять какую-то (хотя бы частичную) функциональность, даже если API партнёра недоступен полностью; + * наконец, настроить автоматическое переключение на другого партнёра или альтернативное API. + ##### Реализуйте функциональность своего API поверх публичных интерфейсов Часто можно увидеть антипаттерн: разработчики API используют внутренние непубличные реализации тех или иных методов взамен существующих в их API публичных. Это происходит по двум причинам: diff --git a/src/ru/drafts/04-Раздел III. API как продукт/02.md b/src/ru/drafts/04-Раздел III. API как продукт/02.md index b731ace..b0a642f 100644 --- a/src/ru/drafts/04-Раздел III. API как продукт/02.md +++ b/src/ru/drafts/04-Раздел III. API как продукт/02.md @@ -38,7 +38,19 @@ Доступ к API в этом случае может быть безусловно платным, хотя чаще встречаются смешанные модели монетизации: API предоставляется бесплатно до достижения какого-то лимита либо при соблюдении определённых условий (например, для некоммерческих проектов). В отдельных случаях API предоставляется бесплатно с минимальными ограничениями с целью популяризации определённой платформы (например, Apple Maps). -Отдельно здесь следует отметить B2B-сервисы. Так как провайдеру сервиса выгодно дать клиенту как можно более разнообразные возможности, а клиенту, в свою очередь, часто требуется максимально гибко использовать имеющуюся функциональность, часто предоставление API — оптимальный выход для обеих сторон. Крупным компаниям, располагающим своими отделами разработки, чаще необходимо запрограммировать свои бизнес-процессы через API и интегрировать их со своими внутренними системами. Часто таким B2B-сервисом выступает сама компания — разработчик API, если собственные сервисы компании строятся поверх API же, а внешний API существует в дополнение к внутреннему (или вовсе «на сдачу»). +Отдельно здесь следует отметить B2B-сервисы. Так как провайдеру сервиса выгодно дать клиенту как можно более разнообразные возможности, а клиенту, в свою очередь, часто требуется максимально гибко использовать имеющуюся функциональность, часто предоставление API — оптимальный выход для обеих сторон. Крупным компаниям, располагающим своими отделами разработки, чаще необходимо запрограммировать свои бизнес-процессы через API и интегрировать их со своими внутренними системами. Часто таким B2B-сервисом выступает сама компания — разработчик API, если собственные сервисы компании строятся поверх API же, а внешний API существует в дополнение к внутреннему. + +**NB**: мы всячески не рекомендуем при этом предоставление API «на сдачу», т.е. публикацию внутренних API без какой-либо дополнительной продуктовой и технической подготовки. Главная проблема таких API заключается в том, что интересы партнёров при планировании разработки никак не учитываются, что приводит к множественным проблемам. + * API плохо покрывает основные сценарии интеграции: + * внутренние потребители, как правило, используют вполне конкретный технический стек, и API плохо оптимизирован под любые другие языки программирования / операционные системы / фреймворки; + * внутренние потребители гораздо лучше погружены в контекст, могут посмотреть исходный код или пообщаться напрямую с разработчиком API, и для них порог входа в технологию находится на совершенно другом уровне по сравнению с внешними потребителями; + * документация покрывает какой-то наиболее востребованный внутренними потребителями срез сценариев; + * линейка сервисов API, о которой мы расскажем ниже, зачастую попросту отсутствует, т.к. внутренними потребителям она не нужна. + * Любые ресурсы выделяются в первую очередь на поддержку внутренних потребителей. Это означает, что: + * планы развития API для партнёров совершенно непрозрачны и бывает что попросту абсурдны; очевидные недоработки не исправляются годами; + * техническая поддержка внешних пользователей осуществляется по остаточному принципу. + +Всё это приводит к тому, что наличие внешнего API зачастую работает не в плюс компании, а в минус: фактически, вы предоставляете крайне критически и скептически настроенной аудитории очень плохой продукт. Если у вас нет ресурсов на грамотное развитие API как продукта для внешних пользователей — лучше за него не браться совсем. ##### API = площадка для рекламы @@ -57,7 +69,7 @@ ##### API = инструмент получения обратной связи и UGC -Если компания располагает какими-то большими данными, то оправданной может быть стратегия выпуска публичного API для того, чтобы конечные пользователи вносили исправления в данные или иным образом вовлекались в их разметку. Например, провайдеры картографических API часто разрешают сообщить об ошибке или исправить неточность прямо в стороннем приложении. [А в случае нашего кофейного API мы могли бы собирать обратную связь, как пассивно — стоить рейтинги заведений, например, — так и активно — контактировать с владельцами заведений чтобы помочь им исправить недостатки; находить через UGC ещё не подключенные к API кофейни и проактивно работать с ними.] +Если компания располагает какими-то большими данными, то оправданной может быть стратегия выпуска публичного API для того, чтобы конечные пользователи вносили исправления в данные или иным образом вовлекались в их разметку. Например, провайдеры картографических API часто разрешают сообщить об ошибке или исправить неточность прямо в стороннем приложении. [А в случае нашего кофейного API мы могли бы собирать обратную связь, как пассивно — строить рейтинги заведений, например, — так и активно — контактировать с владельцами заведений чтобы помочь им исправить недостатки; находить через UGC ещё не подключенные к API кофейни и проактивно работать с ними.] ##### Терраформирование diff --git a/src/ru/drafts/04-Раздел III. API как продукт/07. KPI API.md b/src/ru/drafts/04-Раздел III. API как продукт/07. KPI API.md index a9121d1..5cac5e1 100644 --- a/src/ru/drafts/04-Раздел III. API как продукт/07. KPI API.md +++ b/src/ru/drafts/04-Раздел III. API как продукт/07. KPI API.md @@ -35,7 +35,11 @@ #### SLA -Невозможно в этом разделе не упомянуть и о «гигиеническом KPI» — уровне предоставляемых услуг и доступности сервиса. Мы не будем здесь давать детального описания, поскольку SLA API ничем не отличается от SLA других видов цифровых сервисов, отметим лишь то, что следить за ним, разумеется, надо, особенно в случае платных API. Впрочем, во многих случаях провайдеры API обычно ограничиваются достаточно свободным SLA, трактуя тарифицируемые услуги как услуги доступ к информации или лицензирование контента. +Невозможно в этом разделе не упомянуть и о «гигиеническом KPI» — уровне предоставляемых услуг и доступности сервиса. Мы не будем здесь давать детального описания, поскольку SLA API ничем не отличается от SLA других видов цифровых сервисов, отметим лишь то, что следить за ним, разумеется, надо, особенно в случае платных API. Впрочем, во многих случаях провайдеры API обычно ограничиваются достаточно свободным SLA, трактуя тарифицируемые услуги как услуги доступа к информации или лицензирование контента. + +Тем не менее, позволим себе ещё раз напомнить: любые проблемы вашего API автоматически умножаются на количество партнёров, особенно в тех случаях, когда ваш API критически для них важен, т.е. при неработоспособности API становится недоступной основная функциональность сервиса. (Впрочем, по упомянутым выше причинам качество интеграции бо́льшей части партнёров почти неизбежно будет таково, что ошибки в работе их сервисов будут происходить, даже если ваш API не является формально для них критическим — а потому, что разработчики неправильно написали обработку ошибок.) + +Важно отметить, что нагрузку на API, вообще говоря, крайне сложно предсказать. Неоптимальное использование API, т.е. его инициализация в тех разделах приложений, где он в реальности не нужен, может привести к колоссальному росту нагрузки вследствие перемещения одной-единственной строки кода партнёра. «Запас прочности» API-сервис должен быть гораздо выше, чем у обычных пользовательских сервисов — как минимум на уровне, достаточном для поддержания его работоспособности в том случае, если крупнейший партнёр начнёт вызывать API при загрузке любой страницы вебсайта / открытии любого экрана приложения. (Если партнёр уже так делает — то API должен переживать как минимум удвоение этой нагрузки на тот случай, если разработчики случайно начнут инициализировать API дважды.) #### Сравнение с конкурентами