1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2025-03-17 20:42:26 +02:00

various typos and style fixes

This commit is contained in:
Sergey Konstantinov 2020-12-29 16:03:19 +03:00
parent f03047415b
commit 0a0c9cf0b9
5 changed files with 13 additions and 13 deletions

View File

@ -58,7 +58,7 @@ I will translate sections into English at the moment they're ready. Which means
## Contributing
I am accepting inquiries. Fill free to open issues.
I am accepting inquiries. Feel free to open issues.
I am NOT accepting pull requests introducing any new content, since I'm willing to be the only author. I would gratefully accept typo fixes, though.

View File

@ -10,7 +10,7 @@ Backwards compatibility is a feature of the entire API system to be stable in ti
2. What does ‘a long period of time’ mean?
From our point of view, backwars compatibility maintenance period should be reconciled with subject area applications lifetime. Platform LTS periods are a decent guidance in the most cases. Since apps will be rewritten anyway when the platform maintenance period ends, it is reasonable to expect developers to move to the new API version also. In mainstream subject areas (e.g. desktop and mobile operating systems) this period lasts several years.
From our point of view, backwards compatibility maintenance period should be reconciled with subject area applications lifetime. Platform LTS periods are a decent guidance in the most cases. Since apps will be rewritten anyway when the platform maintenance period ends, it is reasonable to expect developers to move to the new API version also. In mainstream subject areas (e.g. desktop and mobile operating systems) this period lasts several years.
From the definition becomes obvious why backwards compatibility needs to be maintained (including taking necessary measures at the API design stage). An outage, full or partial, caused by the API vendor, is an extremely uncomfortable situation for every developer, if not a disaster — especially if they pay money for the API usage.
@ -38,7 +38,7 @@ When you shipped the very first API version, and first clients started to use it
1. If the platform allows for fetching code on-demand, like the good old Web does, and you weren't too lazy to implement that code-on-demand (in a form of a platform SDK — for example, JS API), then the evolution of your API is more or less under your control. Maintaining backwards compatibility effectively means keeping *the client library* backwards compatible. As for client-server interaction, you're free.
It doesn't mean that you can't break backwards compatibility. You still can make a mess with cache control headers or just overlook a bug in the code. Besides, even code-on-demand systems don't get updated instantly. The author of this book faced the situation, when users were deliberately keeping a browser tab open *for weeks* to get rid of updates. But still, you usually don't have to support more than two API versions — the last one and the penuiltimate one. Furthermore, you may try to rewrite previous major version of the library, implementing it upon the actual version API.
It doesn't mean that you can't break backwards compatibility. You still can make a mess with cache control headers or just overlook a bug in the code. Besides, even code-on-demand systems don't get updated instantly. The author of this book faced the situation, when users were deliberately keeping a browser tab open *for weeks* to get rid of updates. But still, you usually don't have to support more than two API versions — the last one and the penultimate one. Furthermore, you may try to rewrite previous major version of the library, implementing it upon the actual version API.
2. If code-on-demand isn't supported or is prohibited by the platform, as in modern mobile operating systems, then the situation becomes more severe. Each client effectively borrows a snapshot of the code, working with your API, frozen at the moment of compilation. Client application updates are scattered over time at much more extent than Web application updates. The most painful thing is that *some clients will never be up to date*, because of one of three reasons:

View File

@ -10,7 +10,7 @@ At any moment in its lifetime, your API is like an iceberg: it comprises an obse
* Revoking the API functionality causes losses. If you've promised to provide some functionality, you will have to do so ‘forever’ (until this API version's maintenance period is over). Pronouncing some functionality deprecated is a tricky thing, potentially alienating your customers.
Rule \#1 is the simplest: if some functionality might be withheld — then never expose it. It might be reformulated like: every entity, every field, every public API method is a *product solution*. There must be solid reasons why some functionality is exposed.
Rule \#1 is the simplest: if some functionality might be withheld — then never expose it. It might be reformulated like: every entity, every field, every public API method is a *product solution*. There must be solid *product* reasons why some functionality is exposed.
##### 2. Avoid gray zones and ambiguities

View File

@ -2,7 +2,7 @@
Как обычно, дадим смысловой определение «обратной совместимости», прежде чем начинать изложение.
Обратная совместимость — это всего свойство системы API быть стабильной во времени. Это значит следующее: **код, написанный разработчиками с использованием вашего API, продолжает работать функционально корректно в течение длительного времени**. К этому определению есть два больших вопроса, и два уточнения к ним.
Обратная совместимость — это свойство всей системы API быть стабильной во времени. Это значит следующее: **код, написанный разработчиками с использованием вашего API, продолжает работать функционально корректно в течение длительного времени**. К этому определению есть два больших вопроса, и два уточнения к ним.
1. Что значит «функционально корректно»?

View File

@ -8,9 +8,9 @@
* Компьютеры существуют, чтобы сложные вещи делать просто, не наоборот. Код, который напишут разработчики поверх вашего API, должен в простых и лаконичных выражениях описывать решение сложной проблемы. Если разработчики вынуждены писать больше кода на вашем API, чем вы написали сами — что-то здесь пошло не так. Возможно, такой API просто не нужен.
* Изъятие функциональности из API невозможно без серьёзных потерь. Если вы пообещали предоставлять какую-то функциональность — вам теперь придётся предоставлять её «вечно» (до окончания поддержки этой мажорной версии API). Объявление функциональности неподдерживаемой очень сложный и чреватый потенциальными конфликтами с потребителем процесс.
* Изъятие функциональности из API невозможно без серьёзных потерь. Если вы пообещали предоставлять какую-то функциональность — вам теперь придётся предоставлять её «вечно» (до окончания поддержки этой мажорной версии API). Объявление функциональности неподдерживаемой очень сложный и чреватый потенциальными конфликтами с потребителем процесс.
Правило №1 самое простое: если какую-то функциональность можно не выставлять наружу — значит, выставлять её не надо. Можно сформулировать и так: каждая сущность, каждое поле, каждый метод в публичном API — это *продуктовое* решение. Должны существовать веские причины, по которым та или иная сущность документирована.
Правило №1 самое простое: если какую-то функциональность можно не выставлять наружу — значит, выставлять её не надо. Можно сформулировать и так: каждая сущность, каждое поле, каждый метод в публичном API — это *продуктовое* решение. Должны существовать веские *продуктовые* причины, по которым та или иная сущность документирована.
##### 2. Избегайте серых зон и недосказанности
@ -19,9 +19,9 @@
Однако достаточно часто разработчики API сами легитимизируют такие серые зоны, например:
* отдают недокументированные поля в ответах эндпойнтов;
* используют непубличную функциональность в примерах кода — в документации, в ответ на обращения пользователей, в выступлениях на конференциях и т.д.;
* используют непубличную функциональность в примерах кода — в документации, в ответ на обращения пользователей, в выступлениях на конференциях и т.д.
Нельзя взять обязательства наполовину. Или вы гарантируете работу этого кода всегда, или не подавайте никаких намеков на то, что такая функциональность существует.
Нельзя принять обязательства наполовину. Или вы гарантируете работу этого кода всегда, или не подавайте никаких намеков на то, что такая функциональность существует.
##### 3. Фиксируйте неявные договорённости
@ -37,7 +37,7 @@ let status = api.getStatus(order.id);
Предположим, что в какой-то момент при масштабировании вашего сервиса вы пришли к асинхронной репликации базы данных и разрешили чтение из реплики. Это приведёт к тому, что после создания заказа следующее обращение к его статусу по id может вернуть `404`, если пришлось на асинхронную реплику, до которой ещё не дошли последние изменения из мастера. Фактически, вы сменили политику консистентности со strong на eventual.
К чему это приведёт? К тому, что код выше перестанет работать. Разработчик создал заказ, пытается получить его статус — и получает ошибку. Очень тяжело предсказать, какую реакцию на эту ошибку предусмотрят разработчики — с большой вероятностью никакую.
К чему это приведёт? К тому, что код выше перестанет работать. Разработчик создал заказ, пытается получить его статус — и получает ошибку. Очень тяжело предсказать, какую реакцию на эту ошибку предусмотрят разработчики — вероятнее всего, никакую.
Вы можете сказать: «Позвольте, но мы нигде и не обещали строгую консистентность!» — и это будет, конечно, неправдой. Вы можете так сказать если, и только если, вы действительно в документации метода `createOrder` явно описали нестрогую консистентность, а все ваши примеры использования SDK написаны как-то так:
@ -58,7 +58,7 @@ if (status) {
}
```
Мы полагаем, что можно не уточнять, что писать код, подобный вышеприведённому ни в коем случае нельзя. Уж если вы действительно предоставляете нестрого консистентное API, то либо операция `createOrder` в SDK должна быть асинхронной и возвращать результат только по готовности всех реплик, либо политика перезапросов должна быть скрыта внутри операции `getStatus`.
Мы полагаем, что можно не уточнять, что писать код, подобный вышеприведённому, ни в коем случае нельзя. Уж если вы действительно предоставляете нестрого консистентное API, то либо операция `createOrder` в SDK должна быть асинхронной и возвращать результат только по готовности всех реплик, либо политика перезапросов должна быть скрыта внутри операции `getStatus`.
Если же нестрогая консистентность не была описана с самого начала — вы не можете внести такие изменения в API. Это эффективный слом обратной совместимости, который к тому же приведёт к огромным проблемам ваших потребителей, поскольку проблема будет воспроизводиться случайным образом.
@ -76,7 +76,7 @@ resolve();
Этот код полагается на то, что callback-функция, переданная в `new Promise` будет выполнена *синхронно*, и переменная `resolve` будет инициализирована к моменту вызова `resolve()`. Однако это конвенция абсолютно ниоткуда не следует: ничто в сигнатуре конструктора `new Promise` не указывает на синхронный вызов callback-а.
Разработчики языка, конечно, могут позволить себе такие фокусы. Однако вы как разработчик API — не можете. Вы должны как минимум задокументировать это поведение и подобрать сигнатуры так, чтобы оно было очевидно; но вообще хорошим советом будет вообще избегать таких конвенций, поскольку они банально неочевидны при прочтении кода, использующего ваше API. Ну и конечно же ни при каких обстоятельствах вы не можете изменить это поведение с синхронного на асинхронное.
Разработчики языка, конечно, могут позволить себе такие фокусы. Однако вы как разработчик API — не можете. Вы должны как минимум задокументировать это поведение и подобрать сигнатуры так, чтобы оно было очевидно; но вообще хорошим советом будет избегать таких конвенций, поскольку они банально неочевидны при прочтении кода, использующего ваше API. Ну и конечно же ни при каких обстоятельствах вы не можете изменить это поведение с синхронного на асинхронное.
**Пример 3**. Представьте, что вы предоставляете API для анимаций, в котором есть две независимые функции:
@ -123,7 +123,7 @@ GET /v1/orders/{id}/events/history
Допустим, в какой-то момент вы решили надёжным клиентам с хорошей историей заказов предоставлять кофе «в кредит», не дожидаясь подтверждения платежа. Т.е. заказ перейдёт в статус "preparing_started", а может и "ready", вообще без события "payment_approved". Вам может показаться, что это изменение является обратно-совместимым — в самом деле, вы же и не обещали никакого конкретного порядка событий. Но это, конечно, не так.
Предположим, что у разработчика (вероятно, бизнес-партнера вашей компании) написан какой-то код, выполняющий какую-то полезную бизнес функцию поверх этих событий — например, аналитика по затратам и доходам. Вполне логично ожидать, что этот код будет оперировать какой-то машиной состояний, которая будет переходить в то или иное состояние в зависимости от получения или неполучения события. Аналитический код наверняка сломается вследствие изменения порядка событий. В лучшем случае разработчик увидит какие-то исключения и будет вынужден разбираться с причиной; в худшем случае партнер будет оперировать неправильной статистикой неопределённое время, пока не найдёт в ней ошибку.
Предположим, что у разработчика (вероятно, бизнес-партнера вашей компании) написан какой-то код, выполняющий какую-то полезную бизнес функцию поверх этих событий — например, строит аналитику по затратам и доходам. Вполне логично ожидать, что этот код будет оперировать какой-то машиной состояний, которая будет переходить в то или иное состояние в зависимости от получения или неполучения события. Аналитический код наверняка сломается вследствие изменения порядка событий. В лучшем случае разработчик увидит какие-то исключения и будет вынужден разбираться с причиной; в худшем случае партнер будет оперировать неправильной статистикой неопределённое время, пока не найдёт в ней ошибку.
Правильным решением было бы во-первых, изначально задокументировать порядок событий и допустимые состояния; во-вторых, продолжать генерировать событие "payment_approved" перед "preparing_started" (если вы приняли решение исполнять такой заказ — значит, по сути, подтвердили платёж) и добавить расширенную информацию о платеже.