1
0
mirror of https://github.com/twirl/The-API-Book.git synced 2025-01-05 10:20:22 +02:00
The-API-Book/docs/API.en.html
2020-12-07 17:39:49 +03:00

182 lines
16 KiB
HTML

<html><head>
<meta charset="utf-8"/>
<title>Sergey Konstantinov. The API</title>
<meta name="author" content="Sergey Konstantinov"/>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=PT+Serif&amp;family=PT+Sans&amp;family=Inconsolata"/>
<style>body {
font-family: 'PT Serif';
font-size: 14pt;
text-align: justify;
}
@media screen {
body {
margin: 20px auto;
max-width: 60%;
}
}
@media print {
h1 {
margin: 4.5in 0 5.2in 0;
}
body {
font-size: 20pt;
}
}
.cc-by-nc {
background: transparent url(https://i.creativecommons.org/l/by-nc/4.0/88x31.png) 0 5px no-repeat;
padding-left: 92px;
}
code, pre {
font-family: Inconsolata, sans-serif;
}
code {
white-space: nowrap;
}
pre {
margin: 1em 0;
padding: 1em;
border-radius: .25em;
border-top: 1px solid rgba(0,0,0,.45);
border-left: 1px solid rgba(0,0,0,.45);
box-shadow: .1em .1em .1em rgba(0,0,0,.45);
page-break-inside: avoid;
}
pre code {
white-space: pre;
}
.page-break {
page-break-after: always;
}
a {
text-decoration: none;
}
h1, h2, h3, h4, h5 {
text-align: left;
font-family: 'PT Sans';
font-weight: bold;
page-break-after: avoid;
}
h1 {
font-size: 200%;
}
h2 {
font-size: 160%;
}
h3 {
font-size: 140%;
}
h4, h5 {
font-size: 120%;
}
@page {
size: 8.5in 11in;
margin: 0.5in;
}
:root {
--main-font: 'PT Serif';
--alt-font: 'PT Serif';
--code-font: Inconsolata;
}</style>
</head><body>
<article><h1>Sergey Konstantinov<br/>The API</h1>
<p class="cc-by-nc">This work is licensed under a <a href="http://creativecommons.org/licenses/by-nc/4.0/">Creative Commons Attribution-NonCommercial 4.0 International License</a>.</p>
<div class="page-break"></div><h2>Introduction</h2><h3 id="chapter1onthestructureofthisbook">Chapter 1. On the Structure of This Book</h3>
<p>The book you're holding in your hands comprises this Introduction and three large sections.</p>
<p>In Section I we'll discuss designing the API as a concept: how to build the architecture properly, from a high-level planning down to final interfaces.</p>
<p>Section II is dedicated to API's lifecycle: how interfaces evolve over time, and how to elaborate the product to match users' needs.</p>
<p>Finally, Section III is more about un-engineering sides of the API, like API marketing, organizing support, and working with a community.</p>
<p>First two sections are the most interesting to engineers, while third section is being more relevant to both engineers and product managers. But we insist that this section is the most important for the API software developer. Since API is the product for engineers, you cannot simply pronounce non-engineering team responsible for its product planning and support. Nobody but you understands more what product features your API is capable of.</p>
<p>Let's start.</p><div class="page-break"></div><h3 id="chapter2theapidefinition">Chapter 2. The API Definition</h3>
<p>Before we start talking about the API design, we need to explicitly define what the API is. Encyclopedia tells us that API is an acronym for ‘Application Program Interface’. This definition is fine, but useless. Much like ‘Man’ definition by Plato: Man stood upright on two legs without feathers. This definition is fine again, but it gives us no understanding what's so important about a Man. (Actually, not ‘fine’ either. Diogenes of Sinope once brought a plucked chicken, saying ‘That's Plato's Man’. And Plato had to add ‘with broad nails’ to his definition.)</p>
<p>What API <em>means</em> apart from the formal definition?</p>
<p>You're possibly reading this book using a Web browser. To make the browser display this page correctly, a bunch of stuff must work correctly: parsing the URL according to the specification; DNS service; TLS handshake protocol; transmitting the data over HTTP protocol; HTML document parsing; CSS document parsing; correct HTML+CSS rendering.</p>
<p>But those are just a tip of an iceberg. To make HTTP protocol work you need the entire network stack (comprising 4-5 or even more different level protocols) work correctly. HTML document parsing is being performed according to hundreds of different specifications. Document rendering calls the underlying operating system API, or even directly graphical processor API. And so on: down to contemporary CISC processor commands implemented on top of microcommands API.</p>
<p>In other words, hundreds or even thousands of different APIs must work correctly to make possible basic actions like viewing a webpage. Contemporary internet technologies simply couldn't exist without these tons of API working fine.</p>
<p><strong>An API is an obligation</strong>. A formal obligation to connect different programmable contexts.</p>
<p>When I'm asked of an example of a well-designed API, I usually show the picture of a Roman viaduct:</p>
<ul>
<li>it interconnects two areas;</li>
<li>backwards compatibility being broken not a single time in two thousand years.</li>
</ul>
<p>What differs between a Roman viaduct and a good API is that APIs presume a contract being <em>programmable</em>. To connect two areas some <em>coding</em> is needed. The goal of this book is to help you in designing APIs which serve their purposes as solidly as a Roman viaduct does.</p>
<p>A viaduct also illustrates another problem of the API design: your customers are engineers themselves. You are not supplying water to end-users: suppliers are plugging their pipes to you engineering structure, building their own structures upon it. From one side, you may provide water access to much more people through them, not spending your time on plugging each individual house to your network. But from other side, you can't control the quality of suppliers' solutions, and you are to be blamed every time there is a water problem caused by their incompetence.</p>
<p>That's why designing the API implies a larger area of responsibilities. <strong>API is a multiplier to both your opportunities and mistakes</strong>.</p><div class="page-break"></div><h3 id="chapter3apiqualitycriteria">Chapter 3. API Quality Criteria</h3>
<p>Before we start laying out the recommendations, we ought to specify what API we consider ‘fine’, and what's the profit of having a ‘fine’ API.</p>
<p>Let's discuss second question first. Obviously, API ‘finesse’ is first of all defined through its capability to solve developers' problems. (One may reasonably say that solving developers' problem might not be the main purpose of offering the API of ours to developers. However, manipulating public opinion is out of this book's author interest. Here we assume that APIs exist primarily to help developers in solving their problems, not for some other covertly declared purposes.)</p>
<p>So, how API design might help the developers? Quite simple: well-designed API must solve their problems in the most efficient and comprehensible manner. Distance from formulating the task to writing working code must be as short as possible. Among other things, it means that:</p>
<ul>
<li>it must be totally obvious out of your API's structure how to solve a task; ideally, developers at first glance should be able to understand, what entities are meant to solve their problem;</li>
<li>the API must be readable; ideally, developers write correct code after just looking at method nomenclature, never bothering about details (especially API implementation details!); it also also very important to mention, that not only problem solution should be obvious, but also possible errors and exceptions;</li>
<li>the API must be consistent; while developing new functionality (i.e. while using unknown new API entities) developers may write new code similar to the code they already wrote using known API concepts, and this new code will work.</li>
</ul>
<p>However static convenience and clarity of APIs is a simple part. After all, nobody seeks for making an API deliberately irrational and unreadable. When we are developing an API, we always start with clear basic concepts. While possessing some experience in designing APIs it's quite hard to make an API core which fails to meet obviousness, readability, and consistency criteria.</p>
<p>Problems begin we we start to expand our API. Adding new functionality sooner or later result in transforming once plain and simple API into a mess of conflicting concepts, and our efforts to maintain backwards compatibility lead to illogical, unobvious and simply bad design solutions. It is partly related to an inability to predict future completely: your understanding of ‘fine’ APIs will change over time, both in objective terms (what problems the API is to solve and what are the best practices) and in subjective ones too (what obviousness, readability and consistency <em>really means</em> regarding your API).</p>
<p>Principles we are explaining below are specifically oriented to make APIs evolve smoothly over time, not being turned into a pile of mixed inconsistent interfaces. It is crucial to understand that this approach isn't free: a necessity to bear in mind all possible extension variants and keep essential growth points mean interface redundancy and possibly excessing abstractions being embedded in the API design. Besides both make developers' work harder. <strong>Providing excess design complexities being reserved for future use makes sense only when this future actually exists for your API. Otherwise it's simply an overengineering.</strong></p><div class="page-break"></div><h3 id="chapter4backwardscompatibility">Chapter 4. Backwards Compatibility</h3>
<p>Backwards compatibility is a temporal characteristics of your API. An obligation to maintain backwards compatibility is the crucial point where API developments differs form software development in general.</p>
<p>Of course, backwards compatibility isn't an absolute. In some subject areas shipping new backwards incompatible API versions is a routine. Nevertheless, every time you deploy new backwards incompatible API version, the developers need to make some non-zero effort to adapt their code to the new API version. In this sense, releasing new API versions puts a sort of a ‘tax’ on customers. They must spend quite real money just to make sure they product continue working.</p>
<p>Of course, large companies which occupy firm market positions could afford implying such a taxation. Furthermore, they may introduce penalties for those who refuse to adapt their code to new API versions, up to disabling their applications.</p>
<p>From our point of view such practice cannot be justified. Don't imply hidden taxes on your customers. If you're able to avoid breaking backwards compatibility — never break it.</p>
<p>Of course, maintaining old API versions is sort of a tax either. Technology changes, and you cannot foresee everything, regardless of how nice your API is initially designed. At some point keeping old API versions results in an inability to provide new functionality and support new platforms, and you will be forced to release new version. But at least you will be able to explain to your customers why they need to make an effort.</p>
<p>We will discuss API lifecycle and version policies in Section II.</p><div class="page-break"></div><h3 id="chapter5onversioning">Chapter 5. On versioning</h3>
<p>Here and throughout we firmly stick to <a href="https://semver.org/">semver</a> principles of versioning:</p>
<ol>
<li>API versions are denoted with three numbers, i.e. <code>1.2.3</code>.</li>
<li>First number (major version) when backwards incompatible changes in the API are shipped.</li>
<li>Second Number (minor version) increases when new functionality is added to the API, keeping backwards compatibility intact.</li>
<li>Third number (patch) increases when new API version contains bug fixes only.</li>
</ol>
<p>Terms ‘major API version’ and ‘new API version, containing backwards incompatible changes to functionality’ are therefore to be considered as equivalent.</p>
<p>In Section II we will discuss versioning policies in more details. In Section I we will just use semver versions designation, specifically <code>v1</code>, <code>v2</code>, etc.</p><div class="page-break"></div><h3 id="chapter6termsandnotationkeys">Chapter 6. Terms and Notation Keys</h3>
<p>Software development is being characterized, among other things, by an existence of many different engineering paradigms, whose adepts sometimes are quite aggressive towards other paradigms' adepts. While writing this book we are deliberately avoiding using terms like ‘method’, ‘object’, ‘function’, and so on, using a neutral term ‘entity’ instead. ‘Entity’ means some atomic functionality unit, like class, method, object, monad, prototype (underline what you think right).</p>
<p>For entity's components we regretfully failed to find a proper term, so we will use words ‘fields’ and ‘methods’.</p>
<p>Most of the examples of APIs in general will be provide in a form of JSON-over-HTTP endpoints. This is some sort of notation which, as we see it, helps to describe concepts in the most comprehensible manner. <code>GET /v1/orders</code> endpoint call could easily be replaced with <code>orders.get()</code> method call, local or remote. JSON could easily be replaced with any other data format. Meaning of assertions shouldn't change.</p>
<p>Let's take a look at the following example:</p>
<pre><code>// Method description
POST /v1/bucket/{id}/some-resource
X-Idempotency-Token: &lt;idempotency token&gt;
{
// This is a single-line comment
"some_parameter": "example value",
}
→ 404 Not Found
Cache-Control: no-cache
{
/* And this is
a multiline comment */
"error_message"
}
</code></pre>
<p>It should be read like:</p>
<ul>
<li>perform a POST-request to a <code>/v1/bucket/{id}/some-resource</code> resource, where <code>{id}</code> is to be replaced with some <code>bucket</code>'s identifier <code>{something}</code> should refer to the nearest term from the left, unless explicitly specified otherwise);</li>
<li>a specific <code>X-Idempotency-Token</code> header is added to the request alongside with standard headers (which we omit);</li>
<li>terms in angle brackets (<code>&lt;idempotency token&gt;</code>) describe the semantic of an entity value (field, header, parameter);</li>
<li>a specific JSON, containing a <code>some_parameter</code> field with <code>example value</code> value and some other unspecified fields (indicated by ellipsis) is being sent as a request body payload;</li>
<li>in response (marked with arrow symbol <code></code>) server returns a <code>404 Not Founds</code> status code; status might be omitted (treat it like <code>200 OK</code> if no status is provided);</li>
<li>response could possibly contain additional notable headers;</li>
<li>response body is a JSON comprising single <code>error_message</code> field; field value absence means that field contains exactly what you expect it should contain — some error message in this case.</li>
</ul>
<p>Some request and response parts might be omitted if they are irrelevant to a topic being discussed.</p>
<p>Simplified notation might be used to avoid redundancies, like <code>POST /v1/bucket/{id}/some-resource</code> <code>{…,"some_parameter",…}</code><code>{ "operation_id" }</code>; request and response bodies might also be omitted.</p>
<p>We will be using expressions like ‘<code>POST /v1/bucket/{id}/some-resource</code> method’ (or simply ‘<code>bucket/some-resource</code> method’, ‘<code>some-resource</code>’ method if no other <code>some-resource</code>s are specified throughout the chapter, so there is no ambiguity) to refer to such endpoint definition.</p>
<p>Apart from HTTP API notation we will employ C-style pseudocode, or, to be more precise, JavaScript-like or Python-like since types are omitted. We assume such imperative structures being readable enough to skip detailed grammar explanations.</p><div class="page-break"></div><h2>The API Design</h2></article>
</body></html>