Skip to content

Commit

Permalink
corrections
Browse files Browse the repository at this point in the history
  • Loading branch information
twirl committed Jun 17, 2023
1 parent 3293fb8 commit 4616abc
Show file tree
Hide file tree
Showing 36 changed files with 303 additions and 329 deletions.
5 changes: 2 additions & 3 deletions src/en/clean-copy/01-Introduction/02.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ In other words, hundreds or even thousands of different APIs must work correctly
When I'm asked for an example of a well-designed API, I usually show a picture of a Roman aqueduct:

[![igorelick @ pixabay](/img/pont-du-gard.jpg "The Pont-du-Gard aqueduct. Built in the 1st century AD")](https://pixabay.com/photos/pont-du-gard-france-aqueduct-bridge-3909998/)

* it interconnects two areas,
* backward compatibility has not been broken even once in two thousand years.
* It interconnects two areas
* Backward compatibility has not been broken even once in two thousand years.

What differs between a Roman aqueduct and a good API is that in the case of APIs, the contract is presumed to be *programmable*. To connect the two areas, *writing some code* is needed. The goal of this book is to help you design APIs that serve their purposes as solidly as a Roman aqueduct does.

Expand Down
10 changes: 5 additions & 5 deletions src/en/clean-copy/01-Introduction/03.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
In the first three sections of this book, we aim to discuss API design in general, not bound to any specific technology. The concepts we describe are equally applicable to, let's say, web services and operating system (OS) APIs.

Still, two main scenarios dominate the stage when we talk about API development:
* developing client-server applications
* developing client SDKs.
* Developing client-server applications
* Developing client SDKs.

In the first case, we almost universally talk about APIs working atop the HTTP protocol. Today, the only notable examples of non-HTTP-based client-server interaction protocols are WebSocket (though it might, and frequently does, work in conjunction with HTTP), MQTT, and highly specialized APIs like media streaming and broadcasting formats.

Expand All @@ -13,15 +13,15 @@ In the first case, we almost universally talk about APIs working atop the HTTP p
Although the technology looks homogeneous because of using the same application-level protocol, in reality, there is significant diversity regarding different approaches to realizing HTTP-based APIs.

**First**, implementations differ in terms of utilizing HTTP capabilities:
* either the client-server interaction heavily relies on the features described in the HTTP standard (or rather standards, as the functionality is split across several different RFCs),
* or HTTP is used as transport, and there is an additional abstraction level built upon it (i.e., the HTTP capabilities, such as the headers and status codes nomenclatures, are deliberately reduced to a bare minimum, and all the metadata is handled by the higher-level protocol).
* Either the client-server interaction heavily relies on the features described in the HTTP standard (or rather standards, as the functionality is split across several different RFCs), or
* HTTP is used as transport, and there is an additional abstraction level built upon it (i.e., the HTTP capabilities, such as the headers and status codes nomenclatures, are deliberately reduced to a bare minimum, and all the metadata is handled by the higher-level protocol).

The APIs that belong to the first category are usually denoted as “REST” or “RESTful” APIs. The second category comprises different RPC formats and some service protocols, for example, SSH.

**Second**, different HTTP APIs rely on different data formats:
* REST APIs and some RPCs ([JSON-RPC](https://www.jsonrpc.org/), [GraphQL](https://graphql.org/), etc.) use the [JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) format (sometimes with some additional endpoints to transfer binary data)
* [gRPC](https://grpc.io/) and some specialized RPC protocols like [Apache Avro](https://avro.apache.org/docs/) utilize binary formats (such as [Protocol Buffers](https://protobuf.dev/), [FlatBuffers](https://flatbuffers.dev/), or Apache Avro's own format)
* finally, some RPC protocols (notably [SOAP](https://www.w3.org/TR/soap12/) and [XML-RPC](http://xmlrpc.com/)) employ the [XML](https://www.w3.org/TR/xml/) data format (which is considered a rather outdated practice by many developers).
* Finally, some RPC protocols (notably [SOAP](https://www.w3.org/TR/soap12/) and [XML-RPC](http://xmlrpc.com/)) employ the [XML](https://www.w3.org/TR/xml/) data format (which is considered a rather outdated practice by many developers).

All the above-mentioned technologies operate in significantly dissimilar paradigms, which give rise to rather hot “holy war” debates among software engineers. However, at the moment this book is being written we observe the choice for general-purpose APIs is reduced to the “REST API (in fact, JSON-over-HTTP) vs. gRPC vs. GraphQL” triad.

Expand Down
14 changes: 7 additions & 7 deletions src/en/clean-copy/01-Introduction/04.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ Before we start laying out the recommendations, we ought to specify what API we
Let's discuss the second question first. Obviously, API “finesse” is primarily defined through its capability to solve developers' and users' problems. (One could reasonably argue that solving problems might not be the main purpose of offering an API to developers. However, manipulating public opinion is not of interest to the author of this book. Here we assume that APIs exist primarily to help people, not for some other covertly declared purposes.)

So, how might a “fine” API design assist developers in solving their (and their users') problems? Quite simply: a well-designed API allows developers to do their jobs in the most efficient and convenient manner. The distance from formulating a task to writing working code must be as short as possible. Among other things, this means that:
* it must be totally obvious from your API's structure how to solve a task
* ideally, developers should be able to understand at first glance, what entities are meant to solve their problem
* the API must be readable
* ideally, developers should write correct code after just looking at the methods' nomenclature, never bothering about details (especially API implementation details!)
* it is also essential to mention that not only should the problem solution (the “happy path”) be obvious, but also possible errors and exceptions (the “unhappy path”)
* the API must be consistent
* while developing new functionality (i.e., while using previously unknown API entities) developers may write new code similar to the code they have already written using the known API concepts, and this new code will work.
* It must be totally obvious from your API's structure how to solve a task:
* Ideally, developers should be able to understand at first glance, what entities are meant to solve their problem
* The API must be readable:
* Ideally, developers should be able to write correct code after just looking at the methods' nomenclature, never bothering about details (especially API implementation details!)
* It is also essential to mention that not only should the problem solution (the “happy path”) be obvious, but also possible errors and exceptions (the “unhappy path”)
* The API must be consistent:
* While developing new functionality (i.e., while using previously unknown API entities) developers may write new code similar to the code they have already written using the known API concepts, and this new code will work.

However, the static convenience and clarity of APIs are simple parts. After all, nobody seeks to make an API deliberately irrational and unreadable. When we develop an API, we always start with clear basic concepts. Providing you have some experience in APIs, it's quite hard to make an API core that fails to meet obviousness, readability, and consistency criteria.

Expand Down
12 changes: 6 additions & 6 deletions src/en/clean-copy/01-Introduction/05.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ However, we must differentiate between the product concept of the API-first appr
The former means that the first (and sometimes the only) step in developing a service is creating an API for it, and we will discuss it in “The API Product” section of this book.

If we talk about the API-first approach in a technical sense, we mean the following: **the contract, i.e. the obligation to connect two programmable contexts, precedes the implementation and defines it**. More specifically, two rules must be respected:
* the contract is developed and committed to in the form of a specification before the functionality is implemented
* if it turns out that the implementation and the contract differ, the implementation is to be fixed, not the contract.
* The contract is developed and committed to in the form of a specification before the functionality is implemented.
* If it turns out that the implementation and the contract differ, the implementation is to be fixed, not the contract.

The “specification” in this context is a formal machine-readable description of the contract in one of the interface definition languages (IDL) — for example, in the form of a Swagger/OpenAPI document or a `.proto` file.

Both rules assert that partner developers' interests are given the highest priority:
* rule \#1 allows partners to write code based on the specification without coordinating the process with the API provider
* the possibility of auto-generating code based on the specification emerges, which might make development significantly less complex and error-prone or even automate it
* the code might be developed without having access to the API
* rule \#2 means partners won't need to change their implementations should some inconsistencies between the specification and the API functionality arise.
* Rule \#1 allows partners to write code based on the specification without coordinating the process with the API provider:
* The possibility of auto-generating code based on the specification emerges, which might make development significantly less complex and error-prone or even automate it
* The code might be developed without having access to the API.
* Rule \#2 means partners won't need to change their implementations should some inconsistencies between the specification and the API functionality arise.

Therefore, for your API consumers, the API-first approach is a guarantee of a kind. However, it only works if the API was initially well-designed. If some irreparable flaws in the specification surface, we would have no other option but to break rule \#2.
16 changes: 8 additions & 8 deletions src/en/clean-copy/01-Introduction/08.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,14 @@ Cache-Control: no-cache
```

It should be read like this:
* a client performs a `POST` request to a `/v1/bucket/{id}/some-resource` resource, where `{id}` is to be replaced with some `bucket`'s identifier (`{something}` notation refers to the nearest term from the left unless explicitly specified otherwise);
* a specific `X-Idempotency-Token` header is added to the request alongside standard headers (which we omit);
* terms in angle brackets (`<idempotency token>`) describe the semantics of an entity value (field, header, parameter);
* a specific JSON, containing a `some_parameter` field and some other unspecified fields (indicated by ellipsis) is being sent as a request body payload;
* in response (marked with an arrow symbol ``) the server returns a `404 Not Found` status code; the status might be omitted (treat it like a `200 OK` if no status is provided);
* the response could possibly contain additional notable headers;
* the response body is a JSON comprising two fields: `error_reason` and `error_message`; field value absence means that the field contains exactly what you expect it should contain — so there is some generic error reason value which we omitted;
* if some token is too long to fit on a single line, we will split it into several lines adding `` to indicate it continues next line.
* A client performs a `POST` request to a `/v1/bucket/{id}/some-resource` resource, where `{id}` is to be replaced with some `bucket`'s identifier (`{something}` notation refers to the nearest term from the left unless explicitly specified otherwise).
* A specific `X-Idempotency-Token` header is added to the request alongside standard headers (which we omit).
* Terms in angle brackets (`<idempotency token>`) describe the semantics of an entity value (field, header, parameter).
* A specific JSON, containing a `some_parameter` field and some other unspecified fields (indicated by ellipsis) is being sent as a request body payload.
* In response (marked with an arrow symbol ``) the server returns a `404 Not Found` status code; the status might be omitted (treat it like a `200 OK` if no status is provided).
* The response could possibly contain additional notable headers.
* The response body is a JSON comprising two fields: `error_reason` and `error_message`. Absence of a value means that the field contains exactly what you expect it should contain — so there is some generic error reason value which we omitted.
* If some token is too long to fit on a single line, we will split it into several lines adding `` to indicate it continues next line.

The term “client” here stands for an application being executed on a user's device, either a native or a web one. The terms “agent” and “user agent” are synonymous with “client.”

Expand Down
8 changes: 4 additions & 4 deletions src/en/clean-copy/02-Section I. The API Design/01.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
### [The API Contexts Pyramid][api-design-context-pyramid]

The approach we use to design APIs comprises four steps:
* defining an application field
* separating abstraction levels
* isolating responsibility areas
* describing final interfaces.
* Defining an application field
* Separating abstraction levels
* Isolating responsibility areas
* Describing final interfaces.

This four-step algorithm actually builds an API from top to bottom, from common requirements and use case scenarios down to a refined nomenclature of entities. In fact, moving this way will eventually conclude with a ready-to-use API, and that's why we value this approach highly.

Expand Down
8 changes: 4 additions & 4 deletions src/en/clean-copy/02-Section I. The API Design/02.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ As for our speculative example, let us imagine that in the nearby future, some t
#### What and How

After finishing all these theoretical exercises, we should proceed directly to designing and developing the API, having a decent understanding of two things:
* *what* we're doing exactly
* *how* we're doing it exactly.
* *What* we're doing exactly
* *How* we're doing it exactly.

In our coffee case, we are:
* providing an API to services with a larger audience so that their users may order a cup of coffee in the most efficient and convenient manner
* abstracting access to coffee machines' “hardware” and developing generalized software methods to select a beverage kind and a location to make an order.
* Providing an API to services with a larger audience so that their users may order a cup of coffee in the most efficient and convenient manner
* Abstracting access to coffee machines' “hardware” and developing generalized software methods to select a beverage kind and a location to make an order.
Loading

0 comments on commit 4616abc

Please sign in to comment.