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
refactoring continued
This commit is contained in:
@ -12,9 +12,9 @@ In other words, hundreds or even thousands of different APIs must work correctly
|
||||
|
||||
**An API is an obligation**. A formal obligation to connect different programmable contexts.
|
||||
|
||||
When I'm asked for an example of a well-designed API, I usually show a picture of a Roman aqueduct:
|
||||
When the author of this book is asked for an example of a well-designed API, he will usually show a picture of a Roman aqueduct:
|
||||
|
||||
[](https://pixabay.com/photos/pont-du-gard-france-aqueduct-bridge-3909998/)
|
||||
[](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.
|
||||
|
||||
|
@ -1,34 +1,20 @@
|
||||
### [An Overview of Existing API Development Solutions][intro-api-solutions-overview]
|
||||
|
||||
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.
|
||||
|
||||
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.
|
||||
|
||||
#### HTTP API
|
||||
|
||||
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).
|
||||
|
||||
The APIs that belong to the first category are usually denoted as “*REST*” or “*RESTful*” APIs. The second category comprises mostly protocols for making remote procedure calls (RPC).
|
||||
|
||||
**Second**, different HTTP APIs rely on different data formats:
|
||||
* REST APIs and some RPC protocols (such as *JSON-RPC*[ref JSON-RPC](https://www.jsonrpc.org/), *GraphQL*[ref GraphQL](https://graphql.org/), etc.) use the *JSON*[ref JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) format (sometimes with some additional endpoints to transfer binary data)
|
||||
* *gRPC*[ref gRPC](https://grpc.io/) and some specialized RPC protocols like *Apache Avro*[ref Apache Avro](https://avro.apache.org/docs/) utilize binary formats (such as *Protocol Buffers*[ref Protocol Buffers](https://protobuf.dev/), *FlatBuffers*[ref FlatBuffers](https://flatbuffers.dev/), or *Apache Avro*'s own format)
|
||||
* Finally, some RPC protocols (notably *SOAP*[ref SOAP](https://www.w3.org/TR/soap12/) and *XML-RPC*[ref XML-RPC](http://xmlrpc.com/)) employ the *XML*[ref Extensible Markup Language (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.
|
||||
|
||||
#### SDKs
|
||||
|
||||
The term “SDK” (stands for “Software Development Kit”) is not, strictly speaking, related to APIs: this is a generic term for a software toolkit. As with “REST,” however, it got some popular reading as a client framework to work with some underlying API. This might be, for example, a wrapper to a client-server API or a UI to some OS API. The major difference from the APIs we discussed in the previous paragraph is that an “SDK” is implemented for a specific programming language and platform to work with some underlying low-level API.
|
||||
|
||||
Unlike client-server APIs, such SDKs can hardly be generalized as each of them is developed for a specific language-platform pair. Interoperable SDKs exist, notably cross-platform mobile (*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), etc.) and desktop (*JavaFX*[ref JavaFX](https://openjfx.io/), *QT*[ref QT](https://www.qt.io/), etc.) frameworks and some highly-specialized solutions (*Unity*[ref Unity](https://docs.unity3d.com/Manual/index.html)). However, they are still narrowly focused on concrete technologies.
|
||||
|
||||
Still, SDKs feature some generality in terms of *the problems they solve*, and Section V of this book will be dedicated to solving these problems of translating contexts and making UI components.
|
||||
### [API Quality Criteria][intro-api-quality]
|
||||
|
||||
Before we start laying out the recommendations for designing API architecture, we ought to specify what constitutes a “high-quality API,” and what the benefits of having a high-quality API are. Quite obviously, the quality of an API is primarily defined through its capability to solve developers' and users' problems. (Let's leave out the part where an API vendor pursues its own goals, not providing a useful product.)
|
||||
|
||||
So, how can a “high-quality” 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 gap between formulating a task and writing working code must be as short as possible. Among other things, this means that:
|
||||
|
||||
* It must be entirely obvious from your API's structure how to solve a task:
|
||||
* Ideally, developers should be able to understand at first glance, which entities are meant to solve their problem.
|
||||
* The API must be readable:
|
||||
* Developers should be able to write correct code simply by examining the methods' nomenclature without becoming entangled in 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 the handling of errors and exceptions (the “unhappy path”).
|
||||
* The API must be consistent:
|
||||
* When developing new functionality (i.e., 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 should work.
|
||||
* It is highly desirable that the API aligns well with the principles and rules of the used platform and framework (if any).
|
||||
|
||||
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.
|
||||
|
||||
Problems begin when we start to expand our API. Adding new functionality sooner or later results in transforming once plain and simple API into a mess of conflicting concepts, and our efforts to maintain backward compatibility will lead to illogical, unobvious, and simply bad design solutions. It is partly related to an inability to predict the future in detail: your understanding of “fine” APIs will change over time, both in objective terms (what problems the API is to solve, and what is best practice) and in subjective terms too (what obviousness, readability, and consistency *really mean* to your API design).
|
||||
|
||||
The principles we are explaining below are specifically oriented towards making APIs evolve smoothly over time, without being turned into a pile of mixed inconsistent interfaces. It is crucial to understand that this approach isn't free: the necessity to bear in mind all possible extension variants and to preserve essential growth points means interface redundancy and possibly excessive abstractions being embedded in the API design. Besides, both make the developers' jobs harder. **Providing excess design complexities being reserved for future use makes sense only if this future actually exists for your API. Otherwise, it's simply overengineering.**
|
||||
|
@ -1,20 +1,11 @@
|
||||
### [API Quality Criteria][intro-api-quality]
|
||||
### [Choosing Solutions for API Development][intro-api-choosing-solutions]
|
||||
|
||||
Before we start laying out the recommendations, we ought to specify what API we consider “fine,” and what the benefits of having a “fine” API are.
|
||||
Let's return to the metaphor of an API as an aqueduct connecting two contexts. While striving to make the use of our construct convenient for customers, we encounter another side of the problem: how would our customers prefer our API to be designed? Are there any widely adopted techniques for connecting water pipes in our subject area?
|
||||
|
||||
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.)
|
||||
In most cases, such standards exist; someone has already designed similar APIs before. The farther apart two contexts are, the more abstractions are invented to connect them, and the more frameworks are developed to work with these abstractions.
|
||||
|
||||
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 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.
|
||||
Utilizing conventional techniques is an essential component of API quality. In areas where an established communication standard exists (such as, for example, the TCP/IP protocol in computer networks), inventing a new one is only viable if you are one hundred percent certain that its advantages will be so obvious that developers will forgive the necessity of learning a new technology to work with the API.
|
||||
|
||||
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.
|
||||
|
||||
Problems begin when we start to expand our API. Adding new functionality sooner or later results in transforming once plain and simple API into a mess of conflicting concepts, and our efforts to maintain backward compatibility will lead to illogical, unobvious, and simply bad design solutions. It is partly related to an inability to predict the future in detail: your understanding of “fine” APIs will change over time, both in objective terms (what problems the API is to solve, and what is best practice) and in subjective terms too (what obviousness, readability, and consistency *really mean* to your API design).
|
||||
|
||||
The principles we are explaining below are specifically oriented towards making APIs evolve smoothly over time, without being turned into a pile of mixed inconsistent interfaces. It is crucial to understand that this approach isn't free: the necessity to bear in mind all possible extension variants and to preserve essential growth points means interface redundancy and possibly excessive abstractions being embedded in the API design. Besides, both make the developers' jobs harder. **Providing excess design complexities being reserved for future use makes sense only if this future actually exists for your API. Otherwise, it's simply overengineering.**
|
||||
However, in many subject areas, such certainty does not exist. On the contrary, various paradigms of API design compete against each other, and you will have to make a choice in favor of one of them (or develop a custom solution). We will discuss two such areas in sections IV and V of this book:
|
||||
* Selecting a paradigm for organizing client-server communication (such as REST API, RPC, GraphQL, etc.) — in the “[Advantages and Disadvantages of HTTP APIs Compared to Alternative Technologies](#http-api-pros-and-cons)” chapter
|
||||
* Selecting an approach to developing UI components — in the “[On Terminology. An Overview of Technologies for UI Development](#sdk-toc-technology-overview)” chapter.
|
@ -1,8 +1,22 @@
|
||||
### [On the HTTP API Concept and Terminology][http-api-concepts]
|
||||
### [On the HTTP API Concept. Paradigms of Developing Client-Server Communication][http-api-concepts]
|
||||
|
||||
The problem of designing HTTP APIs is, unfortunately, one of the most “holywar”-inspiring issues. On one hand, it is one of the most popular technologies but, on the other hand, it is quite complex and difficult to comprehend due to the large and fragmented standard split into many RFCs. As a result, the HTTP specification is doomed to be poorly understood and imperfectly interpreted by millions of software engineers and thousands of textbook writers. Therefore, before proceeding to the useful part of this Section, we must clarify exactly what we are going to discuss.
|
||||
The problem of designing HTTP APIs is, unfortunately, one of the most “holywar”-inspiring issues. On one hand, it is one of the most popular technologies; on the other hand, it is quite complex and difficult to comprehend due to the large and fragmented standard split into many RFCs. As a result, the HTTP specification is doomed to be poorly understood and imperfectly interpreted by millions of software engineers and thousands of textbook writers. Therefore, before proceeding to the useful part of this Section, we must clarify exactly what we are going to discuss.
|
||||
|
||||
It has somehow happened that the entire modern network stack used for developing client-server APIs has been unified in two important points. One of them is the Internet Protocol Suite, which comprises the IP protocol as a base and an additional layer on top of it in the form of either the TCP or UDP protocol. Today, alternatives to the TCP/IP stack are used for a very limited subset of engineering tasks.
|
||||
Let's start with a short historical overview. Performing users' requests on a remote server has been one of the basic tasks in software engineering since mainframes, and it naturally gained additional momentum with the development of ARPANET. The first high-level protocol for network communication worked in the paradigm of sending messages over the network (as an example, see the DEL protocol that was proposed in one of the very first RFCs — RFC-5 published in 1969[ref RFC-5. DEL](https://datatracker.ietf.org/doc/html/rfc5)). However, scholars quickly understood that it would be much more convenient if calling a remote server and accessing remote resources wasn't any different from working with local memory and resources *in terms of function signatures*. This concept was strictly formulated under the name “Remote Procedure Call” (RPC) by Bruce Nelson, an employee of the famous Xerox Palo Alto Research Center in 1981.[ref Nelson, B. J. (1981) Remote procedure call](https://www.semanticscholar.org/paper/Remote-procedure-call-Nelson/c860de40a88090055948b72d04dd79b02195e06b) Nelson was also the co-author of the first practical implementation of the proposed paradigm, namely Sun RPC[ref Birrell, A. D., Nelson, B. J. (1984) Implementing remote procedure calls](https://dl.acm.org/doi/10.1145/2080.357392)[ref RPC: Remote Procedure Call Protocol Specification](https://datatracker.ietf.org/doc/html/rfc1050), which still exists as ONC RPC.
|
||||
|
||||
First widely adopted RPC protocols (such as the aforementioned Sun RPC, Java RMI[ref Remote Method Invocation (RMI)](https://www.oracle.com/java/technologies/javase/remote-method-invocation-home.html), and CORBA[ref CORBA](https://www.corba.org/)) strictly followed the paradigm. The technology allowed achieving exactly what Nelson was writing about — that is, making no difference between local and remote code execution. The “magic” is hidden within tooling that generates the implementation of working with remote servers, and developers don't need to know how the protocol works.
|
||||
|
||||
However, the convenience of using the technology became its Achilles heel:
|
||||
* The requirement of working with remote calls similarly to local ones results in the high complexity of the protocol as it needs to support various features of high-level programming languages.
|
||||
* First-generation RPC protocols dictate the use of specific languages and platforms for both the client and the server:
|
||||
* Sun RPC didn't support the Windows platform.
|
||||
* Java RMI required a Java virtual machine to run.
|
||||
* Some protocols (notably, CORBA) declared the possibility of developing adapters to support any language. However, practically implementing such adapters proved to be complicated.
|
||||
* Proxying requests and sharding data are complicated because these operations require reading and parsing the request body, which could be costly.
|
||||
* The possibility of addressing objects in the remote server's memory just like the local ones implies huge restrictions on scaling such a system.
|
||||
* Interestingly enough, no significant RPC protocol included the memory sharing feature. However, *the possibility* to do this was included in the design of some of them (notably, Sun RPC).
|
||||
|
||||
The ideological crisis of RPC approaches, which became apparent with the rise of mass client-server applications that prioritize scalability and performance over convenience for developers, coincided with another important process — the standardization of network protocols. In the beginning of the '90s, there were still a plethora of different communication formats; however, the network stack had eventually unified around two important attractors. One of them was the Internet Protocol Suite, which comprises the IP protocol as a base and an additional layer on top of it in the form of either the TCP or UDP protocol. Today, alternatives to the TCP/IP stack are used for a very limited subset of engineering tasks.
|
||||
|
||||
However, from a practical standpoint, there is a significant inconvenience that makes using raw TCP/IP protocols much less practical. They operate over IP addresses which are poorly suited for organizing distributed systems:
|
||||
* Firstly, humans are not adept at remembering IP addresses and prefer readable names
|
||||
@ -10,19 +24,37 @@ However, from a practical standpoint, there is a significant inconvenience that
|
||||
|
||||
The domain name system, which allows for assigning human-readable aliases to IP addresses, has proved to be a convenient abstraction with almost universal adoption. Introducing domain names necessitated the development of new protocols at a higher level than TCP/IP. For text (hypertext) data this protocol happened to be HTTP 0.9[ref The Original HTTP as defined in 1991](https://www.w3.org/Protocols/HTTP/AsImplemented.html) developed by Tim Berners-Lee and published in 1991. Besides enabling the use of network node names, HTTP also provided another useful abstraction: assigning separate addresses to endpoints working on the same network node.
|
||||
|
||||
Initially, the protocol was very simple and merely described a method of retrieving a document by establishing a TCP/IP connection to the server and passing a string in the `GET document_address` format. Subsequently, the protocol was enhanced by the URL standard for document addresses. After that, the protocol evolved rapidly: new verbs, response statuses, headers, data types, and other features emerged in a short time.
|
||||
Initially, the protocol was very simple and merely described a method of retrieving a document by establishing a TCP/IP connection to the server and passing a string in the `GET document_address` format. Subsequently, the protocol was enhanced by the Universal Resource Locator (URL) standard for document addresses. After that, the protocol evolved rapidly: new verbs, response statuses, headers, data types, and other features emerged in a short time.
|
||||
|
||||
HTTP was developed to transfer hypertext which poorly fits for developing program interfaces. However, loose HTML quickly evolved into strict and machine-readable XML, which became one of the most widespread standards for describing API calls. Starting from the 2000s, XML was gradually replaced by much simpler and interoperable JSON. Today, when we talk about HTTP APIs, we usually refer to interfaces for transmitting data and executing remote calls in JSON format over the HTTP protocol.
|
||||
HTTP was developed to transfer hypertext which poorly fits for developing program interfaces. However, loose HTML quickly evolved into strict and machine-readable XML, which became one of the most widespread standards for describing API calls. (Starting from the 2000s, XML was gradually replaced by much simpler and interoperable JSON.)
|
||||
|
||||
On one hand, HTTP was a simple and easily understandable protocol to make arbitrary calls to remote servers using their domain names. On the other hand, it quickly gained a wide range of extensions beyond its base functionality. Eventually, HTTP became another “attractor” where all the network technology stacks converge. Most API calls within TCP/IP networks are made through the HTTP protocol. However, unlike the TCP/IP case, it is each developer's own choice which parts of the functionality provided by the HTTP protocol and its numerous extensions they are going to use. For example, gRPC and GraphQL work on top of HTTP but employ a limited subset of its capabilities.
|
||||
On one hand, HTTP was a simple and easily understandable protocol for making arbitrary calls to remote servers using their domain names. On the other hand, it quickly gained a wide range of extensions beyond its base functionality. Eventually, HTTP became another “attractor” where all the network technology stacks converge. Most API calls within TCP/IP networks are made through the HTTP protocol. However, unlike the TCP/IP case, it is each developer's own choice which parts of the functionality provided by the HTTP protocol and its numerous extensions they are going to use. Remarkably enough, HTTP was a full antithesis to RPC as it does not provide any native wrappers to make remote calls, let alone memory sharing. Instead, HTTP provided some useful concepts to improve the scalability of client-server systems, such as managing caches out of the box and the idea of transparent proxies.
|
||||
|
||||
However, the term “HTTP API” is not always a synonym for “any API that utilizes the HTTP protocol.” When we refer to HTTP APIs, we *rather* imply it is used not as a third additional quasi-transport layer protocol (as it happens in the case of gRPC and GraphQL) but as an application-level protocol, meaning its components (such as URL, headers, HTTP verbs, status codes, caching policies, etc.) are used according to their respective semantics. We also likely imply that some textual data format (JSON or XML) is used to describe procedure calls.
|
||||
As a result, starting from the mid-'90s, RPC frameworks were gradually abolished in favor of a new approach, to which Roy Fielding in his doctoral dissertation of 2001 gave the name “Representational State Transfer” or “REST” (to be discussed in the corresponding chapter). In the new paradigm, the relations between data and operations on it were inversed:
|
||||
* Clients do not call procedures on a remote server, passing the call parameters. Instead, they provide an abstract address (a *locator*) of a data fragment (a *resource*) to which the operation is to be applied.
|
||||
* The list of operations is restricted to a limited and standardized number of actions with clearly defined semantics.
|
||||
* The client and the server are independent and, in principle, do not share any state — any parameters needed to fulfill the operation must be transmitted explicitly.
|
||||
* There could be several intermediary agents, such as proxies or gateways, between the client and the server, which should not affect the interaction protocol in any way.
|
||||
* If URLs contain all important parameters (resource identifiers, in particular), it is relatively easy to organize data sharding.
|
||||
* The server marks the caching options for the responses (*resource representations*). The client (and intermediary proxies) can cache the data according to these markings.
|
||||
|
||||
**NB**: switching from architectures where clients and servers are tightly coupled to resource-oriented stateless solutions created the concept of designing client-server APIs as it became mandatory to specify *the contract* between the server and the client. In the early RPC paradigm, referring to API design makes no sense, as the code that developers were writing effectively *was* the interaction API, and developers had no need to care about underlying protocols.
|
||||
|
||||
Although the new approach appeared quite convenient from the perspective of developing highly performant services, the problem of working with declarative APIs in imperative programming languages did not go away. Additionally, the once simple standard quickly became a Frankenstein monster, stitched together from dozens of various fragmented sub-standards. We don't think we will exaggerate if we say no developer in the world knows the entire HTTP standard with all its additional RFCs.
|
||||
|
||||
Starting from the 2010s, there has been an ongoing boom of new-generation RPC technologies — although it would be more correct to say “composite technologies” — that are convenient to use in imperative programming languages (as they come with the necessary tooling to effectively use code generation), interoperable (working on top of fully standardized protocols that do not depend on any specific language), and scalable (providing the abstracted notion of shared resources and disallowing remote memory access).
|
||||
|
||||
Today, a modern API that follows the REST architectural style and a modern RPC protocol *ideologically* differ only in their approaches to marking cacheable data and addressing. In the former, a resource is the addressing unit while the operation parameters are provided in addition; in the latter, the name of the operation is addressable while the identifiers of the resources are passed as additional parameters.
|
||||
|
||||
In the next chapter, we will discuss specific widely adopted technologies, but here we need to emphasize an important fact: **almost all modern high-level protocols (with MQTT being a notable exception) work on top of the HTTP protocol**. So most modern RPC technologies *are* at the same time HTTP APIs.
|
||||
|
||||
However, the term “HTTP API” is not usually treated as a synonym for “any API that utilizes the HTTP protocol.” When we refer to HTTP APIs, we *rather* imply that HTTP is used not as a third additional quasi-transport layer protocol (as it happens in the case of second-generation RPC protocols) but as an application-level protocol, meaning its components (such as URL, headers, HTTP verbs, status codes, caching policies, etc.) are used according to their respective semantics. We also likely imply that some textual data format (JSON or XML) is used to describe procedure calls.
|
||||
|
||||
In this Section, we will discuss client-server APIs with the following properties:
|
||||
* The interaction protocol is HTTP version 1.1 or higher
|
||||
* The data format is JSON (excluding endpoints specifically designed to provide data in other formats, usually files)
|
||||
* The endpoints (resources) are identified by their URLs in accordance with the standard
|
||||
* The semantics of HTTP calls match the specification
|
||||
* None of the Web standards are intentionally violated.
|
||||
* None of the Web standards is intentionally violated.
|
||||
|
||||
We will refer to such APIs as “HTTP APIs” or “JSON-over-HTTP APIs.” We understand that this is a loose interpretation of the term, but we prefer to live with that rather than using the phrase “JSON-over-HTTP endpoints utilizing the semantics described in the HTTP and URL standards” each time.
|
||||
We will refer to such APIs as “HTTP APIs” or “JSON-over-HTTP APIs.” We understand that this is a loose interpretation of the term, but we prefer to live with that rather than using phrases like “JSON-over-HTTP endpoints utilizing the semantics described in the HTTP and URL standards” or “a JSON-over-HTTP API complying with the REST architectural constraints” each time. As for the term “REST API,” it lacks a consistent definition (as we will discuss in the corresponding chapter), so we would avoid using it as well.
|
@ -1,4 +1,4 @@
|
||||
### [On the Content of This Section][sdk-toc]
|
||||
### [On Terminology. An Overview of Technologies for UI Development][sdk-toc-technology-overview]
|
||||
|
||||
As we mentioned in the Introduction, the term “SDK” (which stands for “Software Development Kit”) lacks concrete meaning. The common understanding is that an SDK differs from an API as it provides both program interfaces and tools to work with them. This definition is hardly satisfactory as today any technology is likely to be provided with a bundled toolset.
|
||||
|
||||
|
@ -4,10 +4,13 @@
|
||||
|
||||
Как же дизайн API может помочь разработчику? Очень просто: API должен решать задачи *максимально удобно и понятно*. Путь разработчика от формулирования своей задачи до написания работающего кода должен быть максимально коротким. Это, в том числе, означает, что:
|
||||
|
||||
* из структуры вашего API должно быть максимально очевидно, как решить ту или иную задачу; в идеале разработчику должно быть достаточно одного взгляда на документацию, чтобы понять, с помощью каких сущностей следует решать поставленную задачу;
|
||||
* API должен быть читаемым: в идеале разработчик, просто глядя в номенклатуру методов, сразу пишет правильный код, не углубляясь в детали (особенно — детали реализации!);
|
||||
* из структуры вашего API должно быть максимально очевидно, как решить ту или иную задачу;
|
||||
* разработчику должно быть достаточно одного взгляда на документацию, чтобы понять, с помощью каких сущностей следует решать поставленную задачу;
|
||||
* API должен быть читаемым:
|
||||
* в идеале, разработчик, просто глядя в номенклатуру методов, сразу пишет правильный код, не углубляясь в детали (особенно — детали реализации!);
|
||||
* немаловажно уточнить, что из интерфейсов объектов должно быть понятно не только решение задачи, но и возможные ошибки и исключения;
|
||||
* API должен быть консистентен: при разработке новой функциональности, т.е. при обращении к каким-то незнакомым сущностям в API, разработчик может действовать по аналогии с уже известными ему концепциями API, и его код будет работать;
|
||||
* API должен быть консистентен:
|
||||
* при разработке новой функциональности, т.е. при обращении к каким-то незнакомым сущностям в API, разработчик может действовать по аналогии с уже известными ему концепциями API, и его код будет работать;
|
||||
* желательно при этом, чтобы API соответствовал принципам и правилам платформы и используемого фреймворка.
|
||||
|
||||
Однако статическое удобство и понятность API — это относительно простая часть. В конце концов, никто не стремится специально сделать API нелогичным и нечитаемым — всегда при разработке мы начинаем с каких-то понятных базовых концепций. При минимальном опыте проектирования сложно сделать ядро API, не удовлетворяющее критериям очевидности, читаемости и консистентности.
|
||||
|
@ -4,8 +4,8 @@
|
||||
|
||||
В абсолютном большинстве случаев такие стандарты есть: кто-то уже разрабатывал похожие API раньше. Чем дальше отстоят друг от друга два контекста, тем большее количество разнообразных абстракций и фреймворков работы с ними будет существовать.
|
||||
|
||||
Использование привычных механизмов — важная составляющая качества API. Там, где есть устоявшийся стандарт взаимодействия (скажем, протокол TCP/IP), изобретать свой собственный стандарт рекомендуется только в тех случаях, когда вы стопроцентно уверены, что преимущества нового подхода будут настолько очевидны, что вам простят необходимость изучить новую технологию для работы с API.
|
||||
Использование привычных механизмов — важная составляющая качества API. Там, где есть устоявшийся стандарт взаимодействия (скажем, протокол TCP/IP в компьютерных сетях), изобретать свой собственный стандарт рекомендуется только в тех случаях, когда вы стопроцентно уверены, что преимущества нового подхода будут настолько очевидны, что вам простят необходимость изучить новую технологию для работы с API.
|
||||
|
||||
Во многих областях, однако, подобной определённости нет; напротив, существуют многочисленные конкурирующие друг с другом парадигмы разработки API, и вам придётся сделать выбор в пользу одной из них (или в пользу разработки собственного подхода). Две такие области мы рассмотрим в разделах IV и V настоящей книги:
|
||||
* выбор парадигмы организации клиент-серверного взаимодействия (REST API, RPC, GraphQL) — в главе «[Преимущества и недостатки HTTP API в сравнении с альтернативными технологиями](#http-api-pros-and-cons)»;
|
||||
* выбор подхода к написанию UI компонентов — в главе «[Терминология. Обзор технологий разработки SDK](#sdk-toc)».
|
||||
* выбор подхода к написанию UI компонентов — в главе «[Терминология. Обзор технологий разработки SDK](#sdk-toc-technology-overview)».
|
@ -2,19 +2,23 @@
|
||||
|
||||
Вопросы организации HTTP API — к большому сожалению, одни из самых «холиварных». Будучи одной из самых популярных и притом весьма непростых в понимании технологий (ввиду большого по объёму и фрагментированного на отдельные RFC стандарта), спецификация HTTP обречена быть плохо понятой и превратно истолкованной миллионами разработчиков и многими тысячами учебных пособий. Поэтому, прежде чем переходить непосредственно к полезной части настоящего раздела, мы обязаны дать уточнения, о чём же всё-таки пойдёт речь.
|
||||
|
||||
Начнём с небольшой исторической справки. Выполнение запросов пользователя на удалённом сервере — одна из базовых задач в программировании ещё со времён мейнфреймов, естественным образом получившая новый импульс развития с появлением ARPANET. Хотя первые высокоуровневые протоколы сетевого взаимодействия работали в терминах отправки по сети сообщений (см., например, протокол DEL предложенный в одном из самых первых RFC — RFC-5 от 1969 года[ref RFC-5. DEL](https://datatracker.ietf.org/doc/html/rfc5)), довольно быстро теоретики пришли к мысли, что было бы гораздо удобнее, если бы обращение к удалённому узлу сети и использование удалённых ресурсов *с точки зрения сигнатуры вызова* ничем не отличались от обращения к локальной памяти и локальным ресурсам. Эту концепцию строго сформулировал под названием Remote Procedure Call (RPC) в 1981 году сотрудник знаменитой лаборатории XEROX в Пало-Альто Брюс Нельсон[ref Nelson, B. J. (1981) Remote procedure call](https://www.semanticscholar.org/paper/Remote-procedure-call-Nelson/c860de40a88090055948b72d04dd79b02195e06b) и он же был соавтором первой практической имплементации предложенной парадигмы — Sun RPC[ref Birrell, A. D., Nelson, B. J. (1984) Implementing remote procedure calls](https://dl.acm.org/doi/10.1145/2080.357392)[ref RPC: Remote Procedure Call Protocol Specification](https://datatracker.ietf.org/doc/html/rfc1050), существующей и сегодня под названием ONC RPC.
|
||||
Начнём с небольшой исторической справки. Выполнение запросов пользователя на удалённом сервере — одна из базовых задач в программировании ещё со времён мейнфреймов, естественным образом получившая новый импульс развития с появлением ARPANET. Хотя первые высокоуровневые протоколы сетевого взаимодействия работали в терминах отправки по сети сообщений (см., например, протокол DEL предложенный в одном из самых первых RFC — RFC-5 от 1969 года[ref RFC-5. DEL](https://datatracker.ietf.org/doc/html/rfc5)), довольно быстро теоретики пришли к мысли, что было бы гораздо удобнее, если бы обращение к удалённому узлу сети и использование удалённых ресурсов *с точки зрения сигнатуры вызова* ничем не отличались от обращения к локальной памяти и локальным ресурсам. Эту концепцию строго сформулировал под названием Remote Procedure Call (RPC) в 1981 году сотрудник знаменитой лаборатории Xerox в Пало-Альто Брюс Нельсон[ref Nelson, B. J. (1981) Remote procedure call](https://www.semanticscholar.org/paper/Remote-procedure-call-Nelson/c860de40a88090055948b72d04dd79b02195e06b) и он же был соавтором первой практической имплементации предложенной парадигмы — Sun RPC[ref Birrell, A. D., Nelson, B. J. (1984) Implementing remote procedure calls](https://dl.acm.org/doi/10.1145/2080.357392)[ref RPC: Remote Procedure Call Protocol Specification](https://datatracker.ietf.org/doc/html/rfc1050), существующей и сегодня под названием ONC RPC.
|
||||
|
||||
Первые широко распространённые сетевые протоколы — такие как упомянутый Sun RPC, Java RMI[ref Remote Method Invocation (RMI)](https://www.oracle.com/java/technologies/javase/remote-method-invocation-home.html), CORBA[ref CORBA](https://www.corba.org/) — чётко следовали парадигме RPC. Технология позволила сделать именно то, о чём писал Нельсон — не различать локальное и удалённое исполнение кода. Вся «магия» скрыта внутри обвязки, которая генерирует имплементации удалённых функций, а с форматом передачи данных разработчик и вовсе не сталкивается.
|
||||
Первые широко распространённые RPC-протоколы — такие как упомянутый Sun RPC, Java RMI[ref Remote Method Invocation (RMI)](https://www.oracle.com/java/technologies/javase/remote-method-invocation-home.html), CORBA[ref CORBA](https://www.corba.org/) — чётко следовали парадигме. Технология позволила сделать именно то, о чём писал Нельсон — не различать локальное и удалённое исполнение кода. Вся «магия» скрыта внутри обвязки, которая генерирует имплементацию работы с удалённым сервером, а с форматом передачи данных разработчик и вовсе не сталкивается.
|
||||
|
||||
Удобство использования технологии, однако, оказалось её же Ахиллесовой пятой:
|
||||
* требование работы с удалёнными вызовами как с локальными приводит к высокой сложности протокола в силу необходимости поддерживать разнообразные возможности высокоуровневых языков программирования;
|
||||
* RPC первого поколения диктуют выбор языка и платформы и для клиента, и для сервера;
|
||||
* Sun RPC не работал под Windows, Java RMI требовал виртуальную машину Java для работы;
|
||||
* RPC первого поколения диктуют выбор языка и платформы и для клиента, и для сервера:
|
||||
* Sun RPC не работал под Windows;
|
||||
* Java RMI требовал виртуальную машину Java для работы;
|
||||
* некоторые протоколы (CORBA, в частности) декларировали возможность разработки адаптеров для любого языка программирования, однако фактическая имплементация поддержки произвольных языков оказалась весьма сложной;
|
||||
* возможность адресовать объекты в памяти удалённого сервера так же, как и локальные накладывает огромные ограничения на масштабирование такой системы;
|
||||
* проксирование запросов и шардирование данных осложнено необходимостью вычитывать и разбирать тело запроса, что может быть ресурсоёмко;
|
||||
* возможность адресовать объекты в памяти удалённого сервера так же, как и локальные накладывает также огромные ограничения на масштабирование такой системы;
|
||||
* любопытно, что ни один заметный RPC-протокол управление разделяемой памятью не реализовал, но сама *возможность* в протоколы (в частности, Sun RPC) была заложена.
|
||||
|
||||
Одновременно с идеологическим кризисом RPC-подходов первого поколения, который стал особенно заметен с появлением массовых клиент-серверных приложений, где производительность намного важнее удобства разработчиков, происходит и другой процесс — стандартизации сетевых протоколов. Ещё в начале 90-х наблюдалось огромное разнообразие форматов взаимодействия, но постепенно сетевой стек в двух точках практических полностью унифицировался. Одна из них — это Internet protocol suite, состоящий из базового протокола IP и надстройки в виде TCP или UDP над ним. На сегодняшний день альтернативы TCP/IP используются в чрезвычайно ограниченном спектре задач, и средний разработчик практически не сталкивается ни с каким другим сетевым стеком. Однако у TCP/IP с прикладной точки зрения есть существенный недостаток — он оперирует поверх системы IP-адресов, которые плохо подходят для организации распределённых систем:
|
||||
Одновременно с идеологическим кризисом RPC-подходов первого поколения, который стал особенно заметен с появлением массовых клиент-серверных приложений, где производительность намного важнее удобства разработчиков, происходит и другой процесс — стандартизации сетевых протоколов. Ещё в начале 90-х наблюдалось огромное разнообразие форматов взаимодействия, но постепенно сетевой стек в двух точках практических полностью унифицировался. Одна из них — это Internet protocol suite, состоящий из базового протокола IP и надстройки в виде TCP или UDP над ним. На сегодняшний день альтернативы TCP/IP используются в чрезвычайно ограниченном спектре задач, и средний разработчик практически не сталкивается ни с каким другим сетевым стеком.
|
||||
|
||||
Однако у TCP/IP с прикладной точки зрения есть существенный недостаток — он оперирует поверх системы IP-адресов, которые плохо подходят для организации распределённых систем:
|
||||
* во-первых, люди не запоминают IP-адреса и предпочитают оперировать «говорящими» именами;
|
||||
* во-вторых, IP-адрес является технической сущностью, связанной с узлом сети, а разработчики хотели бы иметь возможность добавлять и изменять узлы, не нарушая работы своих приложений.
|
||||
|
||||
@ -27,17 +31,22 @@ HTTP появился изначально для передачи размеч
|
||||
Поскольку, с одной стороны, HTTP был простым и понятным протоколом, позволяющим осуществлять произвольные запросы к удаленным серверам по их доменным именам, и, с другой стороны, быстро оброс почти бесконечным количеством разнообразных расширений над базовой функциональностью, он стал второй точкой, к которой сходятся сетевые технологии: практически все запросы к API внутри TCP/IP-сетей осуществляются по протоколу HTTP (и даже если используется альтернативный протокол, запросы в нём всё равно зачастую оформлены в виде HTTP-пакетов просто ради удобства). HTTP, однако, идеологически совершенно противоположен RPC, поскольку не предполагает ни нативной обвязки для функций удалённого вызова, ни тем более разделяемого доступа к памяти. Зато HTTP предложил несколько очень удобных концепций для наращивания производительности серверов, такие как управление кэшированием из коробки и концепцию прозрачных прокси.
|
||||
|
||||
В итоге в середине 1990-х годов происходит постепенный отказ от RPC-фреймворков первого поколения в пользу нового подхода, который позднее Рой Филдинг в своей диссертации 2001 года обобщит под названием «Representational State Transfer» или «REST» (о чём мы поговорим чуть позже в соответствующей главе). В новой парадигме отношения между данными и операциями над ними переворачиваются с ног на голову:
|
||||
* клиент не вызывает процедуры на сервере с передачей параметров — клиент указывает серверу абстрактный адрес фрагмента данных (*ресурса*), к которому он хочет применить операцию;
|
||||
* клиент не вызывает процедуры на сервере с передачей параметров — клиент указывает серверу абстрактный адрес (локатор) фрагмента данных (*ресурса*), к которому он хочет применить операцию;
|
||||
* сам список операций лимитирован и стандартизирован, семантика их чётко определена в стандарте;
|
||||
* клиент и сервер независимы и *принципиально* не имеют никакого разделяемого состояния; все необходимые для исполнения операции параметры должны быть переданы явно;
|
||||
* между клиентом и сервером может находиться множество промежуточных узлов сети (прокси и гейтвеев), что не влияет на протокол;
|
||||
* если все важные параметры операции (в частности, идентификаторы ресурсов) включены в URL, данные легко можно шардировать без серьёзных затрат;
|
||||
* сервер размечает параметры кэширования передаваемых данных (представления ресурсов), клиент (и промежуточные прокси) имеют право кэшировать данные в соответствии с разметкой.
|
||||
|
||||
**NB**: отказ от архитектур, где клиент и сервер жёстко связаны, в пользу ресурсоориентированных stateless подходов собственно породил понятие дизайна клиент-серверного API, поскольку потребовалось зафиксировать *контракт* между клиентом и сервером. В парадигме ранних RPC-фреймворков говорить о дизайне API бессмысленно, поскольку написанный разработчиком код и есть API взаимодействия, а с нижележащими протоколами разработчику и вовсе не было нужды знакомиться.
|
||||
|
||||
Хотя новый подход оказался весьма удачным с точки зрения разработки высокопроизводительных серверов, проблемы неудобства работы с декларативными API из императивных языков никуда не делось. К тому же изначально простой стандарт быстро превратился в монстра Франкенштейна, сшитого из десятков разных подстандартов. Не думаем, что сильно ошибёмся, если скажем, что стандарт HTTP целиком со всеми многочисленными дополнительными RFC не знает полностью ни один человек.
|
||||
Хотя новый подход оказался весьма удачным с точки зрения разработки высокопроизводительных сервисов, проблемы неудобства работы с декларативными API из императивных языков никуда не делось. К тому же изначально простой стандарт быстро превратился в монстра Франкенштейна, сшитого из десятков разных подстандартов. Не думаем, что сильно ошибёмся, если скажем, что стандарт HTTP целиком со всеми многочисленными дополнительными RFC не знает полностью ни один человек.
|
||||
|
||||
Начиная с конца 2010-х годов мы наблюдаем расцвет RPC-технологий нового поколения — или, вернее было бы сказать, комбинированных технологий, которые одновременно и удобны в применении в императивных языках программирования (поставляются с обвязкой, позволяющей эффективно использовать кодогенерацию), и интероперабельны (работают поверх строго стандартизированных протоколов, которые не зависят от конкретного языка программирования), и масштабируемы (абстрагируют понятие ресурса и не предоставляют прямого доступа к памяти сервера). Фактически, на сегодня *идеологически* разница между современным API, следующим классическому архитектурному стилю REST, и современным RPC заключается лишь в разметке кэшируемых данных и принципах адресации: в первом случае единицей доступа является ресурс (а параметры операции передаются дополнительно), а во втором — имя операции (а адреса ресурсов, над которыми она выполняется, передаются дополнительно). Мы рассмотрим конкретные технологии разработки таких API в следующей главе, но отметим, что почти все они (за единственным заметным исключением в виде MQTT) работают поверх протокола HTTP.
|
||||
Начиная с конца 2010-х годов мы наблюдаем расцвет RPC-технологий нового поколения — или, вернее было бы сказать, комбинированных технологий, которые одновременно и удобны в применении в императивных языках программирования (поставляются с обвязкой, позволяющей эффективно использовать кодогенерацию), и интероперабельны (работают поверх строго стандартизированных протоколов, которые не зависят от конкретного языка программирования), и масштабируемы (абстрагируют понятие ресурса и не предоставляют прямого доступа к памяти сервера).
|
||||
|
||||
Фактически, на сегодня *идеологически* разница между современным API, следующим классическому архитектурному стилю REST, и современным RPC заключается лишь в разметке кэшируемых данных и принципах адресации: в первом случае единицей доступа является ресурс (а параметры операции передаются дополнительно), а во втором — имя операции (а адреса ресурсов, над которыми она выполняется, передаются дополнительно).
|
||||
|
||||
Мы рассмотрим конкретные технологии разработки таких API в следующей главе, но отметим здесь важный момент: почти все они (за исключением разве что MQTT) работают поверх протокола HTTP. Так что большинство современных RPC-протоколов *одновременно* является HTTP API.
|
||||
|
||||
Тем не менее, *обычно* словосочетание «HTTP API» используется не просто в значении «любой API, использующий протокол HTTP»; говоря «HTTP API» мы *скорее* подразумеваем, что он используется не как дополнительный третий протокол транспортного уровня, а именно как протокол уровня приложения, то есть составляющие протокола (такие как: URL, заголовки, HTTP-глаголы, статусы ответа, политики кэширования и т.д.) используются в соответствии с их семантикой, определённой в стандартах. *Обычно* также подразумевается, что в HTTP API использует какой-то из текстовых форматов передачи данных (JSON, XML) для описания вызовов.
|
||||
|
||||
@ -48,4 +57,4 @@ HTTP появился изначально для передачи размеч
|
||||
* семантика вызовов HTTP-эндпойнтов соответствует спецификации;
|
||||
* никакие из веб-стандартов нигде не нарушается специально.
|
||||
|
||||
Такое API мы будем для краткости называть просто «HTTP API» или «JSON-over-HTTP API». Мы понимаем, что такое использование терминологически некорректно, но писать каждый раз «JSON-over-HTTP эндпойнты, утилизирующие семантику, описанную в стандартах HTTP и URL» не представляется возможным.
|
||||
**Такое API мы будем для краткости называть просто «HTTP API» или «JSON-over-HTTP API»**. Мы понимаем, что такое использование терминологически не полностью корректно, но писать каждый раз «JSON-over-HTTP эндпойнты, утилизирующие семантику, описанную в стандартах HTTP и URL» или «JSON-over-HTTP API, соответствующий архитектурным ограничениям REST» не представляется возможным. Что касается термина «REST API», то как мы покажем дальше, у него нет консистентного определения, поэтому его использования мы также стараемся избегать.
|
||||
|
@ -1,14 +1,14 @@
|
||||
### [Преимущества и недостатки HTTP API в сравнении с альтернативными технологиями][http-api-pros-and-cons]
|
||||
|
||||
Как мы обсудили в предыдущей главе, в настоящий момент выбор технологии для разработки таких API сводится к выбору либо ресурсоориентированного подхода (то, что принято называть «REST API»; напомним, мы будем использовать для таких API обозначение «HTTP API»), либо одного из современных RPC-протоколов. Как мы отмечали, *концептуально* разница не очень значительна; однако *технически* разные фреймворки используют протокол совершенно по-разному:
|
||||
Как мы обсудили в предыдущей главе, в настоящий момент выбор технологии для разработки клиент-серверных API сводится к выбору либо ресурсоориентированного подхода (то, что принято называть «REST API», а мы, напомним, будем использовать термин «HTTP API»), либо одного из современных RPC-протоколов. Как мы отмечали, *концептуально* разница не очень значительна; однако *технически* разные фреймворки используют протокол совершенно по-разному:
|
||||
|
||||
**Во-первых**, разные фреймворки опираются на разные форматы передаваемых данных:
|
||||
* REST API и некоторые RPC (JSON-RPC[ref JSON-RPC](https://www.jsonrpc.org/), GraphQL[ref GraphQL](https://graphql.org/)) полагаются в основном на формат JSON[ref JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) (опционально дополненный передачей бинарных файлов);
|
||||
* HTTP API и некоторые RPC (JSON-RPC[ref JSON-RPC](https://www.jsonrpc.org/), GraphQL[ref GraphQL](https://graphql.org/)) полагаются в основном на формат JSON[ref JSON](https://www.ecma-international.org/publications-and-standards/standards/ecma-404/) (опционально дополненный передачей бинарных файлов);
|
||||
* gRPC[ref gRPC](https://grpc.io), а также Apache Avro[ref Apache Avro](https://avro.apache.org/docs/) и другие специализированные RPC-протоколы полагаются на бинарные форматы (такие как Protocol Buffers[ref Protocol Buffers](https://protobuf.dev/), FlatBuffers[ref FlatBuffers](https://flatbuffers.dev/) и собственный формат Apache Avro);
|
||||
* наконец, некоторые RPC-протоколы (SOAP[ref SOAP](https://www.w3.org/TR/soap12/), XML-RPC[ref XML-RPC](http://xmlrpc.com/)) используют для передачи данных формат XML[ref Extensible Markup Language (XML)](https://www.w3.org/TR/xml/) (что многими разработчиками сегодня воспринимается скорее как устаревшая практика).
|
||||
|
||||
**Во-вторых**, существующие реализации различаются подходом к утилизации протокола HTTP:
|
||||
* либо клиент-серверное взаимодействие опирается на описанные в стандарте HTTP возможности — этот подход характеризует «REST API»;
|
||||
* либо клиент-серверное взаимодействие опирается на описанные в стандарте HTTP возможности — этот подход характеризует HTTP API;
|
||||
* либо HTTP утилизируется как транспорт, и поверх него выстроен дополнительный уровень абстракции (т.е. возможности HTTP, такие как номенклатура ошибок или заголовков, сознательно редуцируются до минимального уровня, а вся мета-информация переносится на уровень вышестоящего протокола) — этот подход характерен для RPC протоколов.
|
||||
|
||||
У читателя может возникнуть резонный вопрос — а почему вообще существует такая дихотомия: одни API полагаются на стандартную семантику HTTP, другие полностью от неё отказываются в пользу новоизобретённых стандартов, а третьи существуют где-то посередине. Например, если мы посмотрим на формат ответа в JSON-RPC[ref JSON-RPC 2.0 Specification. Response object](https://www.jsonrpc.org/specification#response_object), то мы обнаружим, что он легко мог бы быть заменён на стандартные средства протокола HTTP. Вместо
|
||||
|
@ -1,4 +1,4 @@
|
||||
### [Терминология. Обзор технологий разработки SDK][sdk-toc]
|
||||
### [Терминология. Обзор технологий разработки SDK][sdk-toc-technology-overview]
|
||||
|
||||
Как мы отмечали во Введении, аббревиатура «SDK» («Software Development Kit»), как и многие из обсуждавшихся ранее терминов, не имеет конкретного значения. Считается, что SDK отличается от API тем, что помимо программных интерфейсов содержит и готовые инструменты для работы с ними. Определение это, конечно, лукавое, поскольку почти любая технология сегодня идёт в комплекте со своим набором инструментов.
|
||||
|
||||
|
Reference in New Issue
Block a user